[{"data":1,"prerenderedAt":783},["ShallowReactive",2],{"docs-page-\u002Fdocs\u002Flogging":3,"docs-surround-\u002Fdocs\u002Flogging":774},{"id":4,"title":5,"body":6,"description":767,"extension":768,"meta":769,"navigation":320,"path":770,"seo":771,"stem":772,"__hash__":773},"docs\u002Fdocs\u002F2.logging.md","Logging",{"type":7,"value":8,"toc":755},"minimark",[9,13,18,22,111,120,124,127,182,187,196,272,278,353,357,364,384,387,391,406,413,481,485,501,505,508,566,569,603,617,620,651,655,662,751],[10,11,5],"h1",{"id":12},"logging",[14,15,17],"h2",{"id":16},"log-levels","Log levels",[19,20,21],"p",{},"The SDK exposes five log levels, each with a corresponding method:",[23,24,25,41],"table",{},[26,27,28],"thead",{},[29,30,31,35,38],"tr",{},[32,33,34],"th",{},"Method",[32,36,37],{},"Level",[32,39,40],{},"Use for",[42,43,44,59,72,85,98],"tbody",{},[29,45,46,53,56],{},[47,48,49],"td",{},[50,51,52],"code",{},"lumber.debug()",[47,54,55],{},"debug",[47,57,58],{},"Verbose diagnostic information",[29,60,61,66,69],{},[47,62,63],{},[50,64,65],{},"lumber.info()",[47,67,68],{},"info",[47,70,71],{},"Normal operational events",[29,73,74,79,82],{},[47,75,76],{},[50,77,78],{},"lumber.warn()",[47,80,81],{},"warn",[47,83,84],{},"Unexpected but recoverable situations",[29,86,87,92,95],{},[47,88,89],{},[50,90,91],{},"lumber.error()",[47,93,94],{},"error",[47,96,97],{},"Failures that need attention",[29,99,100,105,108],{},[47,101,102],{},[50,103,104],{},"lumber.fatal()",[47,106,107],{},"fatal",[47,109,110],{},"Critical failures, app may not continue",[19,112,113,116,117,119],{},[50,114,115],{},"lumber.log()"," is an alias for ",[50,118,65],{},".",[14,121,123],{"id":122},"the-data-argument","The data argument",[19,125,126],{},"Every log method takes an optional second argument, an object of data to attach to the log:",[128,129,134],"pre",{"className":130,"code":131,"language":132,"meta":133,"style":133},"language-typescript shiki shiki-themes github-dark","lumber.info('User signed in', {\n  userId: user.id,\n  email: user.email,\n  ip: req.ip,\n})\n","typescript","",[50,135,136,158,164,170,176],{"__ignoreMap":133},[137,138,141,145,148,151,155],"span",{"class":139,"line":140},"line",1,[137,142,144],{"class":143},"s95oV","lumber.",[137,146,68],{"class":147},"svObZ",[137,149,150],{"class":143},"(",[137,152,154],{"class":153},"sU2Wk","'User signed in'",[137,156,157],{"class":143},", {\n",[137,159,161],{"class":139,"line":160},2,[137,162,163],{"class":143},"  userId: user.id,\n",[137,165,167],{"class":139,"line":166},3,[137,168,169],{"class":143},"  email: user.email,\n",[137,171,173],{"class":139,"line":172},4,[137,174,175],{"class":143},"  ip: req.ip,\n",[137,177,179],{"class":139,"line":178},5,[137,180,181],{"class":143},"})\n",[183,184,186],"h3",{"id":185},"keys-with-their-own-place","Keys with their own place",[19,188,189,190,195],{},"Four top-level keys of the data argument aren't metadata. The SDK sends them as fields of their own (see ",[191,192,194],"a",{"href":193},"\u002Fdocs\u002Fhttp-api#the-payload","the payload","), and the Lumber app shows them apart from the metadata:",[23,197,198,211],{},[26,199,200],{},[29,201,202,205,208],{},[32,203,204],{},"Field",[32,206,207],{},"Type",[32,209,210],{},"Description",[42,212,213,228,242,257],{},[29,214,215,220,225],{},[47,216,217],{},[50,218,219],{},"requestId",[47,221,222],{},[50,223,224],{},"string",[47,226,227],{},"Ties a log to a specific HTTP request",[29,229,230,235,239],{},[47,231,232],{},[50,233,234],{},"traceId",[47,236,237],{},[50,238,224],{},[47,240,241],{},"Distributed trace identifier",[29,243,244,249,254],{},[47,245,246],{},[50,247,248],{},"context",[47,250,251],{},[50,252,253],{},"LogContext",[47,255,256],{},"Structured context, merged over the automatic context (pid, hostname, env, and caller code\u002Fline\u002Fcolumn when enabled)",[29,258,259,264,269],{},[47,260,261],{},[50,262,263],{},"metadata",[47,265,266],{},[50,267,268],{},"Record\u003Cstring, unknown>",[47,270,271],{},"The metadata itself, for keys that would otherwise clash with the three above",[19,273,274,275,277],{},"Every other key goes into the log's metadata, and nothing inside ",[50,276,263],{}," is special:",[128,279,281],{"className":130,"code":280,"language":132,"meta":133,"style":133},"\u002F\u002F These are equivalent\nlumber.error('DB query failed', { duration: 1200, table: 'users' })\n\nlumber.error('DB query failed', {\n  metadata: { duration: 1200, table: 'users' },\n})\n",[50,282,283,289,316,322,334,348],{"__ignoreMap":133},[137,284,285],{"class":139,"line":140},[137,286,288],{"class":287},"sAwPA","\u002F\u002F These are equivalent\n",[137,290,291,293,295,297,300,303,307,310,313],{"class":139,"line":160},[137,292,144],{"class":143},[137,294,94],{"class":147},[137,296,150],{"class":143},[137,298,299],{"class":153},"'DB query failed'",[137,301,302],{"class":143},", { duration: ",[137,304,306],{"class":305},"sDLfK","1200",[137,308,309],{"class":143},", table: ",[137,311,312],{"class":153},"'users'",[137,314,315],{"class":143}," })\n",[137,317,318],{"class":139,"line":166},[137,319,321],{"emptyLinePlaceholder":320},true,"\n",[137,323,324,326,328,330,332],{"class":139,"line":172},[137,325,144],{"class":143},[137,327,94],{"class":147},[137,329,150],{"class":143},[137,331,299],{"class":153},[137,333,157],{"class":143},[137,335,336,339,341,343,345],{"class":139,"line":178},[137,337,338],{"class":143},"  metadata: { duration: ",[137,340,306],{"class":305},[137,342,309],{"class":143},[137,344,312],{"class":153},[137,346,347],{"class":143}," },\n",[137,349,351],{"class":139,"line":350},6,[137,352,181],{"class":143},[14,354,356],{"id":355},"async-queue","Async queue",[19,358,359,360,363],{},"Logging is non-blocking. When you call any ",[50,361,362],{},"lumber.*()"," method:",[365,366,367,371,374,381],"ol",{},[368,369,370],"li",{},"The payload is encrypted synchronously (in-process, zero network)",[368,372,373],{},"The encrypted entry is pushed onto an internal async queue",[368,375,376,377,380],{},"The queue drains in the background, sending one ",[50,378,379],{},"POST \u002Flogs"," request per log, one at a time",[368,382,383],{},"Your code continues immediately",[19,385,386],{},"This means log calls never add latency to your request path.",[183,388,390],{"id":389},"queue-behavior","Queue behavior",[392,393,394,397,400,403],"ul",{},[368,395,396],{},"Logs are sent one at a time, in the order they were enqueued",[368,398,399],{},"A network error or any non-2xx response counts as a failed attempt. Each log gets up to 5 attempts, waiting 1s, 2s, 4s, then 8s between them. After the fifth failure the log is dropped and a warning is printed to the console",[368,401,402],{},"While a log is being retried, the logs behind it wait their turn",[368,404,405],{},"The queue is in memory, so logs are lost if the process exits before they drain",[19,407,408,409,412],{},"Always call ",[50,410,411],{},"lumber.flush()"," during graceful shutdown:",[128,414,416],{"className":130,"code":415,"language":132,"meta":133,"style":133},"process.on('SIGTERM', async () => {\n  await lumber.flush()\n  process.exit(0)\n})\n",[50,417,418,447,461,477],{"__ignoreMap":133},[137,419,420,423,426,428,431,434,438,441,444],{"class":139,"line":140},[137,421,422],{"class":143},"process.",[137,424,425],{"class":147},"on",[137,427,150],{"class":143},[137,429,430],{"class":153},"'SIGTERM'",[137,432,433],{"class":143},", ",[137,435,437],{"class":436},"snl16","async",[137,439,440],{"class":143}," () ",[137,442,443],{"class":436},"=>",[137,445,446],{"class":143}," {\n",[137,448,449,452,455,458],{"class":139,"line":160},[137,450,451],{"class":436},"  await",[137,453,454],{"class":143}," lumber.",[137,456,457],{"class":147},"flush",[137,459,460],{"class":143},"()\n",[137,462,463,466,469,471,474],{"class":139,"line":166},[137,464,465],{"class":143},"  process.",[137,467,468],{"class":147},"exit",[137,470,150],{"class":143},[137,472,473],{"class":305},"0",[137,475,476],{"class":143},")\n",[137,478,479],{"class":139,"line":172},[137,480,181],{"class":143},[14,482,484],{"id":483},"encryption","Encryption",[19,486,487,488,492,493,495,496,500],{},"The SDK encrypts each log with ",[191,489,491],{"href":490},"\u002Fdocs\u002Fencryption","AES-256-GCM"," (authenticated encryption, one of the ciphers HTTPS uses) before it's queued, using the encryption key in your API key, and sends Lumber only the token. The level is the one field Lumber can read, so it can filter by level and decide when to send push alerts. ",[191,494,484],{"href":490}," covers what Lumber can and can't see, and the ",[191,497,499],{"href":498},"\u002Fdocs\u002Fhttp-api","HTTP API"," the exact format, for sending logs without the SDK.",[14,502,504],{"id":503},"context-capture","Context capture",[19,506,507],{},"By default, the SDK automatically attaches environment context to every log:",[128,509,511],{"className":130,"code":510,"language":132,"meta":133,"style":133},"\u002F\u002F Captured once at lumber.config() time\n{\n  pid: 12345,\n  hostname: 'my-server',\n  env: 'production',\n}\n",[50,512,513,518,523,537,549,561],{"__ignoreMap":133},[137,514,515],{"class":139,"line":140},[137,516,517],{"class":287},"\u002F\u002F Captured once at lumber.config() time\n",[137,519,520],{"class":139,"line":160},[137,521,522],{"class":143},"{\n",[137,524,525,528,531,534],{"class":139,"line":166},[137,526,527],{"class":147},"  pid",[137,529,530],{"class":143},": ",[137,532,533],{"class":305},"12345",[137,535,536],{"class":143},",\n",[137,538,539,542,544,547],{"class":139,"line":172},[137,540,541],{"class":147},"  hostname",[137,543,530],{"class":143},[137,545,546],{"class":153},"'my-server'",[137,548,536],{"class":143},[137,550,551,554,556,559],{"class":139,"line":178},[137,552,553],{"class":147},"  env",[137,555,530],{"class":143},[137,557,558],{"class":153},"'production'",[137,560,536],{"class":143},[137,562,563],{"class":139,"line":350},[137,564,565],{"class":143},"}\n",[19,567,568],{},"To capture the caller file and line number per log:",[128,570,572],{"className":130,"code":571,"language":132,"meta":133,"style":133},"lumber.config({\n  \u002F\u002F ...\n  context: { caller: true },\n})\n",[50,573,574,584,589,599],{"__ignoreMap":133},[137,575,576,578,581],{"class":139,"line":140},[137,577,144],{"class":143},[137,579,580],{"class":147},"config",[137,582,583],{"class":143},"({\n",[137,585,586],{"class":139,"line":160},[137,587,588],{"class":287},"  \u002F\u002F ...\n",[137,590,591,594,597],{"class":139,"line":166},[137,592,593],{"class":143},"  context: { caller: ",[137,595,596],{"class":305},"true",[137,598,347],{"class":143},[137,600,601],{"class":139,"line":172},[137,602,181],{"class":143},[604,605,606],"blockquote",{},[19,607,608,612,613,616],{},[609,610,611],"strong",{},"Note:"," Caller capture uses ",[50,614,615],{},"Error.stack"," parsing and adds minor per-call overhead. Use it in development or low-throughput scenarios.",[19,618,619],{},"To disable context entirely:",[128,621,623],{"className":130,"code":622,"language":132,"meta":133,"style":133},"lumber.config({\n  \u002F\u002F ...\n  context: false,\n})\n",[50,624,625,633,637,647],{"__ignoreMap":133},[137,626,627,629,631],{"class":139,"line":140},[137,628,144],{"class":143},[137,630,580],{"class":147},[137,632,583],{"class":143},[137,634,635],{"class":139,"line":160},[137,636,588],{"class":287},[137,638,639,642,645],{"class":139,"line":166},[137,640,641],{"class":143},"  context: ",[137,643,644],{"class":305},"false",[137,646,536],{"class":143},[137,648,649],{"class":139,"line":172},[137,650,181],{"class":143},[14,652,654],{"id":653},"using-the-class-directly","Using the class directly",[19,656,657,658,661],{},"For multiple isolated clients (e.g., per-tenant logging), use ",[50,659,660],{},"LumberClient"," instead of the singleton:",[128,663,665],{"className":130,"code":664,"language":132,"meta":133,"style":133},"import { LumberClient } from '@infinitetoken\u002Flumber'\n\nconst logger = new LumberClient({\n  apiKey: tenantApiKey,\n})\n\nlogger.info('Tenant action', { tenantId: '123' })\nawait logger.flush()\n",[50,666,667,681,685,704,709,713,717,738],{"__ignoreMap":133},[137,668,669,672,675,678],{"class":139,"line":140},[137,670,671],{"class":436},"import",[137,673,674],{"class":143}," { LumberClient } ",[137,676,677],{"class":436},"from",[137,679,680],{"class":153}," '@infinitetoken\u002Flumber'\n",[137,682,683],{"class":139,"line":160},[137,684,321],{"emptyLinePlaceholder":320},[137,686,687,690,693,696,699,702],{"class":139,"line":166},[137,688,689],{"class":436},"const",[137,691,692],{"class":305}," logger",[137,694,695],{"class":436}," =",[137,697,698],{"class":436}," new",[137,700,701],{"class":147}," LumberClient",[137,703,583],{"class":143},[137,705,706],{"class":139,"line":172},[137,707,708],{"class":143},"  apiKey: tenantApiKey,\n",[137,710,711],{"class":139,"line":178},[137,712,181],{"class":143},[137,714,715],{"class":139,"line":350},[137,716,321],{"emptyLinePlaceholder":320},[137,718,720,723,725,727,730,733,736],{"class":139,"line":719},7,[137,721,722],{"class":143},"logger.",[137,724,68],{"class":147},[137,726,150],{"class":143},[137,728,729],{"class":153},"'Tenant action'",[137,731,732],{"class":143},", { tenantId: ",[137,734,735],{"class":153},"'123'",[137,737,315],{"class":143},[137,739,741,744,747,749],{"class":139,"line":740},8,[137,742,743],{"class":436},"await",[137,745,746],{"class":143}," logger.",[137,748,457],{"class":147},[137,750,460],{"class":143},[752,753,754],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}",{"title":133,"searchDepth":160,"depth":160,"links":756},[757,758,761,764,765,766],{"id":16,"depth":160,"text":17},{"id":122,"depth":160,"text":123,"children":759},[760],{"id":185,"depth":166,"text":186},{"id":355,"depth":160,"text":356,"children":762},[763],{"id":389,"depth":166,"text":390},{"id":483,"depth":160,"text":484},{"id":503,"depth":160,"text":504},{"id":653,"depth":160,"text":654},"Logging with the Node.js SDK: levels, the data argument, the async queue and context capture.","md",{},"\u002Fdocs\u002Flogging",{"title":5,"description":767},"docs\u002F2.logging","hY22sCFBUqkdaB2gYcpVuO-6ANe-9nZFJavMlQmRJCY",[775,779],{"title":776,"path":777,"stem":778,"children":-1},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",{"title":780,"path":781,"stem":782,"children":-1},"Configuration","\u002Fdocs\u002Fconfiguration","docs\u002F3.configuration",1794245210835]