[{"data":1,"prerenderedAt":1683},["ShallowReactive",2],{"docs-page-\u002Fdocs\u002Fnode":3,"docs-surround-\u002Fdocs\u002Fnode":1675},{"id":4,"title":5,"body":6,"description":1668,"extension":1669,"meta":1670,"navigation":103,"path":1671,"seo":1672,"stem":1673,"__hash__":1674},"docs\u002Fdocs\u002F02.node.md","Node.js",{"type":7,"value":8,"toc":1646},"minimark",[9,13,35,40,65,71,75,287,297,310,314,415,423,427,430,468,476,560,566,635,641,644,658,669,692,709,718,728,743,752,763,777,786,789,797,802,945,953,1007,1012,1104,1112,1134,1184,1191,1195,1247,1263,1346,1349,1363,1397,1401,1415,1516,1523,1527,1534,1618,1623,1627,1642],[10,11,5],"h1",{"id":12},"nodejs",[14,15,16,20,21,26,27,30,31,34],"p",{},[17,18,19],"code",{},"@infinitetoken\u002Flumber"," is Lumber's Node.js SDK. It encrypts every log with ",[22,23,25],"a",{"href":24},"\u002Fdocs\u002Fencryption","AES-256-GCM"," before it leaves your process, and sends it from an internal queue, so logging never adds latency to your request path. Encryption and delivery use Node's built-in ",[17,28,29],{},"crypto"," and ",[17,32,33],{},"fetch",", and its one dependency is Lumber's own shared types package, which has none of its own.",[36,37,39],"h2",{"id":38},"install","Install",[41,42,47],"pre",{"className":43,"code":44,"language":45,"meta":46,"style":46},"language-bash shiki shiki-themes github-dark","npm install @infinitetoken\u002Flumber\n","bash","",[17,48,49],{"__ignoreMap":46},[50,51,54,58,62],"span",{"class":52,"line":53},"line",1,[50,55,57],{"class":56},"svObZ","npm",[50,59,61],{"class":60},"sU2Wk"," install",[50,63,64],{"class":60}," @infinitetoken\u002Flumber\n",[14,66,67,68,70],{},"Node.js 18 or later, for its built-in ",[17,69,33],{},".",[36,72,74],{"id":73},"quick-start","Quick start",[41,76,80],{"className":77,"code":78,"language":79,"meta":46,"style":46},"language-typescript shiki shiki-themes github-dark","import lumber from '@infinitetoken\u002Flumber'\n\nlumber.config({ apiKey: process.env.LUMBER_API_KEY! })\n\nlumber.info('Server started', { port: 3000 })\nlumber.warn('Rate limit approaching', { requestId: 'req-123', userId: 456 })\nlumber.error('Payment failed', { traceId: 'trace-abc', orderId: 789 })\n\n\u002F\u002F On graceful shutdown, send what's still queued\nprocess.on('SIGTERM', async () => {\n  await lumber.flush()\n  process.exit(0)\n})\n","typescript",[17,81,82,98,105,127,132,154,181,208,213,220,249,264,281],{"__ignoreMap":46},[50,83,84,88,92,95],{"class":52,"line":53},[50,85,87],{"class":86},"snl16","import",[50,89,91],{"class":90},"s95oV"," lumber ",[50,93,94],{"class":86},"from",[50,96,97],{"class":60}," '@infinitetoken\u002Flumber'\n",[50,99,101],{"class":52,"line":100},2,[50,102,104],{"emptyLinePlaceholder":103},true,"\n",[50,106,108,111,114,117,121,124],{"class":52,"line":107},3,[50,109,110],{"class":90},"lumber.",[50,112,113],{"class":56},"config",[50,115,116],{"class":90},"({ apiKey: process.env.",[50,118,120],{"class":119},"sDLfK","LUMBER_API_KEY",[50,122,123],{"class":86},"!",[50,125,126],{"class":90}," })\n",[50,128,130],{"class":52,"line":129},4,[50,131,104],{"emptyLinePlaceholder":103},[50,133,135,137,140,143,146,149,152],{"class":52,"line":134},5,[50,136,110],{"class":90},[50,138,139],{"class":56},"info",[50,141,142],{"class":90},"(",[50,144,145],{"class":60},"'Server started'",[50,147,148],{"class":90},", { port: ",[50,150,151],{"class":119},"3000",[50,153,126],{"class":90},[50,155,157,159,162,164,167,170,173,176,179],{"class":52,"line":156},6,[50,158,110],{"class":90},[50,160,161],{"class":56},"warn",[50,163,142],{"class":90},[50,165,166],{"class":60},"'Rate limit approaching'",[50,168,169],{"class":90},", { requestId: ",[50,171,172],{"class":60},"'req-123'",[50,174,175],{"class":90},", userId: ",[50,177,178],{"class":119},"456",[50,180,126],{"class":90},[50,182,184,186,189,191,194,197,200,203,206],{"class":52,"line":183},7,[50,185,110],{"class":90},[50,187,188],{"class":56},"error",[50,190,142],{"class":90},[50,192,193],{"class":60},"'Payment failed'",[50,195,196],{"class":90},", { traceId: ",[50,198,199],{"class":60},"'trace-abc'",[50,201,202],{"class":90},", orderId: ",[50,204,205],{"class":119},"789",[50,207,126],{"class":90},[50,209,211],{"class":52,"line":210},8,[50,212,104],{"emptyLinePlaceholder":103},[50,214,216],{"class":52,"line":215},9,[50,217,219],{"class":218},"sAwPA","\u002F\u002F On graceful shutdown, send what's still queued\n",[50,221,223,226,229,231,234,237,240,243,246],{"class":52,"line":222},10,[50,224,225],{"class":90},"process.",[50,227,228],{"class":56},"on",[50,230,142],{"class":90},[50,232,233],{"class":60},"'SIGTERM'",[50,235,236],{"class":90},", ",[50,238,239],{"class":86},"async",[50,241,242],{"class":90}," () ",[50,244,245],{"class":86},"=>",[50,247,248],{"class":90}," {\n",[50,250,252,255,258,261],{"class":52,"line":251},11,[50,253,254],{"class":86},"  await",[50,256,257],{"class":90}," lumber.",[50,259,260],{"class":56},"flush",[50,262,263],{"class":90},"()\n",[50,265,267,270,273,275,278],{"class":52,"line":266},12,[50,268,269],{"class":90},"  process.",[50,271,272],{"class":56},"exit",[50,274,142],{"class":90},[50,276,277],{"class":119},"0",[50,279,280],{"class":90},")\n",[50,282,284],{"class":52,"line":283},13,[50,285,286],{"class":90},"})\n",[14,288,289,290,293,294,296],{},"Call ",[17,291,292],{},"lumber.config()"," once at startup, before any logging; calling a log method before it throws. Every module that imports ",[17,295,19],{}," then logs with the same client.",[14,298,299,300,304,305,309],{},"The API key comes from the Lumber app (see ",[22,301,303],{"href":302},"\u002Fdocs\u002Fgetting-started#create-an-app-in-lumber","Getting Started","). Keep it in an environment variable or your secrets manager, never in source control, and never in an app or game you ship to people (",[22,306,308],{"href":307},"\u002Fdocs\u002Fapi-keys#keep-the-key-on-your-servers","why",").",[36,311,313],{"id":312},"log-methods","Log methods",[315,316,317,333],"table",{},[318,319,320],"thead",{},[321,322,323,327,330],"tr",{},[324,325,326],"th",{},"Method",[324,328,329],{},"Level",[324,331,332],{},"Use for",[334,335,336,350,362,374,386,399],"tbody",{},[321,337,338,344,347],{},[339,340,341],"td",{},[17,342,343],{},"lumber.debug()",[339,345,346],{},"debug",[339,348,349],{},"Verbose diagnostic information",[321,351,352,357,359],{},[339,353,354],{},[17,355,356],{},"lumber.info()",[339,358,139],{},[339,360,361],{},"Normal operational events",[321,363,364,369,371],{},[339,365,366],{},[17,367,368],{},"lumber.warn()",[339,370,161],{},[339,372,373],{},"Unexpected but recoverable situations",[321,375,376,381,383],{},[339,377,378],{},[17,379,380],{},"lumber.error()",[339,382,188],{},[339,384,385],{},"Failures that need attention",[321,387,388,393,396],{},[339,389,390],{},[17,391,392],{},"lumber.fatal()",[339,394,395],{},"fatal",[339,397,398],{},"Critical failures, app may not continue",[321,400,401,406,408],{},[339,402,403],{},[17,404,405],{},"lumber.flush(timeout?)",[339,407],{},[339,409,410,411,414],{},"Waits for queued logs to send; with a timeout in milliseconds, resolves ",[17,412,413],{},"false"," if logs were still queued then",[14,416,417,420,421,70],{},[17,418,419],{},"lumber.log()"," is an alias for ",[17,422,356],{},[36,424,426],{"id":425},"data","Data",[14,428,429],{},"Every log method takes an optional second argument, an object of data to attach to the log:",[41,431,433],{"className":77,"code":432,"language":79,"meta":46,"style":46},"lumber.info('User signed in', {\n  userId: user.id,\n  email: user.email,\n  ip: req.ip,\n})\n",[17,434,435,449,454,459,464],{"__ignoreMap":46},[50,436,437,439,441,443,446],{"class":52,"line":53},[50,438,110],{"class":90},[50,440,139],{"class":56},[50,442,142],{"class":90},[50,444,445],{"class":60},"'User signed in'",[50,447,448],{"class":90},", {\n",[50,450,451],{"class":52,"line":100},[50,452,453],{"class":90},"  userId: user.id,\n",[50,455,456],{"class":52,"line":107},[50,457,458],{"class":90},"  email: user.email,\n",[50,460,461],{"class":52,"line":129},[50,462,463],{"class":90},"  ip: req.ip,\n",[50,465,466],{"class":52,"line":134},[50,467,286],{"class":90},[14,469,470,471,475],{},"Four top-level keys aren't metadata. The SDK sends them as fields of their own (see ",[22,472,474],{"href":473},"\u002Fdocs\u002Fhttp-api#the-payload","the payload","), and the Lumber app shows them apart from the metadata:",[315,477,478,491],{},[318,479,480],{},[321,481,482,485,488],{},[324,483,484],{},"Field",[324,486,487],{},"Type",[324,489,490],{},"Description",[334,492,493,508,522,545],{},[321,494,495,500,505],{},[339,496,497],{},[17,498,499],{},"requestId",[339,501,502],{},[17,503,504],{},"string",[339,506,507],{},"Ties a log to a specific HTTP request",[321,509,510,515,519],{},[339,511,512],{},[17,513,514],{},"traceId",[339,516,517],{},[17,518,504],{},[339,520,521],{},"Distributed trace identifier",[321,523,524,529,534],{},[339,525,526],{},[17,527,528],{},"context",[339,530,531],{},[17,532,533],{},"LogContext",[339,535,536,537,539,540,544],{},"Structured context, merged over the automatic context (see the ",[17,538,528],{}," option under ",[22,541,543],{"href":542},"#options","Options",")",[321,546,547,552,557],{},[339,548,549],{},[17,550,551],{},"metadata",[339,553,554],{},[17,555,556],{},"Record\u003Cstring, unknown>",[339,558,559],{},"The metadata itself, for keys that would otherwise clash with the three above",[14,561,562,563,565],{},"Every other key goes into the log's metadata, and nothing inside ",[17,564,551],{}," is special:",[41,567,569],{"className":77,"code":568,"language":79,"meta":46,"style":46},"\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",[17,570,571,576,601,605,617,631],{"__ignoreMap":46},[50,572,573],{"class":52,"line":53},[50,574,575],{"class":218},"\u002F\u002F These are equivalent\n",[50,577,578,580,582,584,587,590,593,596,599],{"class":52,"line":100},[50,579,110],{"class":90},[50,581,188],{"class":56},[50,583,142],{"class":90},[50,585,586],{"class":60},"'DB query failed'",[50,588,589],{"class":90},", { duration: ",[50,591,592],{"class":119},"1200",[50,594,595],{"class":90},", table: ",[50,597,598],{"class":60},"'users'",[50,600,126],{"class":90},[50,602,603],{"class":52,"line":107},[50,604,104],{"emptyLinePlaceholder":103},[50,606,607,609,611,613,615],{"class":52,"line":129},[50,608,110],{"class":90},[50,610,188],{"class":56},[50,612,142],{"class":90},[50,614,586],{"class":60},[50,616,448],{"class":90},[50,618,619,622,624,626,628],{"class":52,"line":134},[50,620,621],{"class":90},"  metadata: { duration: ",[50,623,592],{"class":119},[50,625,595],{"class":90},[50,627,598],{"class":60},[50,629,630],{"class":90}," },\n",[50,632,633],{"class":52,"line":156},[50,634,286],{"class":90},[14,636,637,638,640],{},"Explicit ",[17,639,551],{}," keys win over colliding keys. Everything, the message included, is encrypted.",[36,642,543],{"id":643},"options",[14,645,646,649,650,653,654,657],{},[17,647,648],{},"lumber.config(options)"," takes these. Calling it again replaces the client: logs still queued in the previous one keep sending in the background, but ",[17,651,652],{},"lumber.flush()"," only waits for the new client's queue, so call ",[17,655,656],{},"await lumber.flush()"," before reconfiguring if you need to be sure they're delivered.",[659,660,662,665,666,668],"h3",{"id":661},"apikey-string-required",[17,663,664],{},"apiKey"," · ",[17,667,504],{}," · required",[14,670,671,672,676,677,679,680,683,684,687,688,70],{},"The API key from your app's ",[673,674,675],"strong",{},"Secrets"," screen in the Lumber app: a 32-character hexadecimal token, a dot, then the app's base64-encoded encryption key. The SDK sends only the token to the API, as a Bearer token, and encrypts all log content with the encryption key before it leaves your process. ",[17,678,292],{}," (and ",[17,681,682],{},"new LumberClient()",") throws a ",[17,685,686],{},"RangeError"," if the encryption key doesn't decode to exactly 32 bytes. See ",[22,689,691],{"href":690},"\u002Fdocs\u002Fapi-keys","API Keys",[41,693,695],{"className":77,"code":694,"language":79,"meta":46,"style":46},"apiKey: process.env.LUMBER_API_KEY!\n",[17,696,697],{"__ignoreMap":46},[50,698,699,701,704,706],{"class":52,"line":53},[50,700,664],{"class":56},[50,702,703],{"class":90},": process.env.",[50,705,120],{"class":119},[50,707,708],{"class":86},"!\n",[659,710,712,665,715,717],{"id":711},"endpoint-string-optional",[17,713,714],{},"endpoint",[17,716,504],{}," · optional",[14,719,720,721,724,725,70],{},"Override the Lumber API base URL (no trailing path). Defaults to the official Lumber API, ",[17,722,723],{},"https:\u002F\u002Fapi.lumber.infinitetoken.com",". Useful for routing through a proxy. The SDK posts each log to ",[17,726,727],{},"{endpoint}\u002Flogs",[41,729,731],{"className":77,"code":730,"language":79,"meta":46,"style":46},"endpoint: 'https:\u002F\u002Fyour-proxy.example.com'\n",[17,732,733],{"__ignoreMap":46},[50,734,735,737,740],{"class":52,"line":53},[50,736,714],{"class":56},[50,738,739],{"class":90},": ",[50,741,742],{"class":60},"'https:\u002F\u002Fyour-proxy.example.com'\n",[659,744,746,665,749,717],{"id":745},"timeout-number-optional",[17,747,748],{},"timeout",[17,750,751],{},"number",[14,753,754,755,759,760,70],{},"How long to wait, in milliseconds, for Lumber to answer one log before counting the attempt as failed and trying again (see ",[22,756,758],{"href":757},"#delivery","Delivery","). Defaults to ",[17,761,762],{},"10000",[41,764,766],{"className":77,"code":765,"language":79,"meta":46,"style":46},"timeout: 5000\n",[17,767,768],{"__ignoreMap":46},[50,769,770,772,774],{"class":52,"line":53},[50,771,748],{"class":56},[50,773,739],{"class":90},[50,775,776],{"class":119},"5000\n",[659,778,780,665,783,717],{"id":779},"console-boolean-consoleoptions-optional",[17,781,782],{},"console",[17,784,785],{},"boolean | ConsoleOptions",[14,787,788],{},"Echo logs to stdout and stderr, handy during development. Off by default.",[14,790,791,796],{},[673,792,793],{},[17,794,795],{},"true",": all levels, message only, colored prefix when TTY.",[14,798,799],{},[673,800,801],{},"Object form:",[41,803,805],{"className":77,"code":804,"language":79,"meta":46,"style":46},"console: {\n  levels?: Partial\u003CRecord\u003C'debug' | 'info' | 'warn' | 'error' | 'fatal', boolean>>\n  prefix?: boolean    \u002F\u002F show [DEBUG]\u002F[INFO]\u002Fetc label, default true\n  color?: boolean     \u002F\u002F ANSI colors when TTY, default true\n  data?: boolean      \u002F\u002F append metadata after message, default false\n  pretty?: boolean    \u002F\u002F JSON.stringify extras with indentation, default false\n  timestamp?: boolean | 'time' | 'iso' | 'relative'\n}\n",[17,806,807,814,863,876,889,902,914,940],{"__ignoreMap":46},[50,808,809,811],{"class":52,"line":53},[50,810,782],{"class":56},[50,812,813],{"class":90},": {\n",[50,815,816,819,822,825,828,831,833,836,839,842,844,847,849,852,854,857,860],{"class":52,"line":100},[50,817,818],{"class":90},"  levels",[50,820,821],{"class":86},"?:",[50,823,824],{"class":90}," Partial",[50,826,827],{"class":86},"\u003C",[50,829,830],{"class":90},"Record",[50,832,827],{"class":86},[50,834,835],{"class":60},"'debug'",[50,837,838],{"class":86}," |",[50,840,841],{"class":60}," 'info'",[50,843,838],{"class":86},[50,845,846],{"class":60}," 'warn'",[50,848,838],{"class":86},[50,850,851],{"class":60}," 'error'",[50,853,838],{"class":86},[50,855,856],{"class":60}," 'fatal'",[50,858,859],{"class":90},", boolean",[50,861,862],{"class":86},">>\n",[50,864,865,868,870,873],{"class":52,"line":107},[50,866,867],{"class":90},"  prefix",[50,869,821],{"class":86},[50,871,872],{"class":90}," boolean    ",[50,874,875],{"class":218},"\u002F\u002F show [DEBUG]\u002F[INFO]\u002Fetc label, default true\n",[50,877,878,881,883,886],{"class":52,"line":129},[50,879,880],{"class":90},"  color",[50,882,821],{"class":86},[50,884,885],{"class":90}," boolean     ",[50,887,888],{"class":218},"\u002F\u002F ANSI colors when TTY, default true\n",[50,890,891,894,896,899],{"class":52,"line":134},[50,892,893],{"class":90},"  data",[50,895,821],{"class":86},[50,897,898],{"class":90}," boolean      ",[50,900,901],{"class":218},"\u002F\u002F append metadata after message, default false\n",[50,903,904,907,909,911],{"class":52,"line":156},[50,905,906],{"class":90},"  pretty",[50,908,821],{"class":86},[50,910,872],{"class":90},[50,912,913],{"class":218},"\u002F\u002F JSON.stringify extras with indentation, default false\n",[50,915,916,919,921,924,927,930,932,935,937],{"class":52,"line":183},[50,917,918],{"class":90},"  timestamp",[50,920,821],{"class":86},[50,922,923],{"class":90}," boolean ",[50,925,926],{"class":86},"|",[50,928,929],{"class":60}," 'time'",[50,931,838],{"class":86},[50,933,934],{"class":60}," 'iso'",[50,936,838],{"class":86},[50,938,939],{"class":60}," 'relative'\n",[50,941,942],{"class":52,"line":210},[50,943,944],{"class":90},"}\n",[14,946,947],{},[673,948,949,952],{},[17,950,951],{},"timestamp"," values:",[315,954,955,965],{},[318,956,957],{},[321,958,959,962],{},[324,960,961],{},"Value",[324,963,964],{},"Output",[334,966,967,982,994],{},[321,968,969,977],{},[339,970,971,973,974],{},[17,972,795],{}," or ",[17,975,976],{},"'time'",[339,978,979],{},[17,980,981],{},"14:32:01.452 ",[321,983,984,989],{},[339,985,986],{},[17,987,988],{},"'iso'",[339,990,991],{},[17,992,993],{},"2024-01-15T14:32:01.452Z ",[321,995,996,1001],{},[339,997,998],{},[17,999,1000],{},"'relative'",[339,1002,1003,1006],{},[17,1004,1005],{},"+0.452s "," (since process start)",[14,1008,1009],{},[673,1010,1011],{},"Example development config:",[41,1013,1015],{"className":77,"code":1014,"language":79,"meta":46,"style":46},"lumber.config({\n  apiKey: process.env.LUMBER_API_KEY!,\n  console: {\n    timestamp: 'time',\n    color: true,\n    data: true,\n    pretty: true,\n    levels: { debug: process.env.NODE_ENV !== 'production' },\n  },\n})\n",[17,1016,1017,1026,1038,1043,1052,1061,1070,1079,1095,1100],{"__ignoreMap":46},[50,1018,1019,1021,1023],{"class":52,"line":53},[50,1020,110],{"class":90},[50,1022,113],{"class":56},[50,1024,1025],{"class":90},"({\n",[50,1027,1028,1031,1033,1035],{"class":52,"line":100},[50,1029,1030],{"class":90},"  apiKey: process.env.",[50,1032,120],{"class":119},[50,1034,123],{"class":86},[50,1036,1037],{"class":90},",\n",[50,1039,1040],{"class":52,"line":107},[50,1041,1042],{"class":90},"  console: {\n",[50,1044,1045,1048,1050],{"class":52,"line":129},[50,1046,1047],{"class":90},"    timestamp: ",[50,1049,976],{"class":60},[50,1051,1037],{"class":90},[50,1053,1054,1057,1059],{"class":52,"line":134},[50,1055,1056],{"class":90},"    color: ",[50,1058,795],{"class":119},[50,1060,1037],{"class":90},[50,1062,1063,1066,1068],{"class":52,"line":156},[50,1064,1065],{"class":90},"    data: ",[50,1067,795],{"class":119},[50,1069,1037],{"class":90},[50,1071,1072,1075,1077],{"class":52,"line":183},[50,1073,1074],{"class":90},"    pretty: ",[50,1076,795],{"class":119},[50,1078,1037],{"class":90},[50,1080,1081,1084,1087,1090,1093],{"class":52,"line":210},[50,1082,1083],{"class":90},"    levels: { debug: process.env.",[50,1085,1086],{"class":119},"NODE_ENV",[50,1088,1089],{"class":86}," !==",[50,1091,1092],{"class":60}," 'production'",[50,1094,630],{"class":90},[50,1096,1097],{"class":52,"line":215},[50,1098,1099],{"class":90},"  },\n",[50,1101,1102],{"class":52,"line":222},[50,1103,286],{"class":90},[659,1105,1107,665,1109,717],{"id":1106},"context-boolean-contextoptions-optional",[17,1108,528],{},[17,1110,1111],{},"boolean | ContextOptions",[14,1113,1114,1115,1117,1118,236,1121,30,1124,1126,1127,1130,1131,1133],{},"The environment context attached to every log. Defaults to ",[17,1116,795],{},": the ",[17,1119,1120],{},"pid",[17,1122,1123],{},"hostname",[17,1125,1086],{}," (as ",[17,1128,1129],{},"env","), captured once at ",[17,1132,292],{}," time.",[41,1135,1137],{"className":77,"code":1136,"language":79,"meta":46,"style":46},"{\n  pid: 12345,\n  hostname: 'my-server',\n  env: 'production',\n}\n",[17,1138,1139,1144,1156,1168,1180],{"__ignoreMap":46},[50,1140,1141],{"class":52,"line":53},[50,1142,1143],{"class":90},"{\n",[50,1145,1146,1149,1151,1154],{"class":52,"line":100},[50,1147,1148],{"class":56},"  pid",[50,1150,739],{"class":90},[50,1152,1153],{"class":119},"12345",[50,1155,1037],{"class":90},[50,1157,1158,1161,1163,1166],{"class":52,"line":107},[50,1159,1160],{"class":56},"  hostname",[50,1162,739],{"class":90},[50,1164,1165],{"class":60},"'my-server'",[50,1167,1037],{"class":90},[50,1169,1170,1173,1175,1178],{"class":52,"line":129},[50,1171,1172],{"class":56},"  env",[50,1174,739],{"class":90},[50,1176,1177],{"class":60},"'production'",[50,1179,1037],{"class":90},[50,1181,1182],{"class":52,"line":134},[50,1183,944],{"class":90},[14,1185,1186,1190],{},[673,1187,1188],{},[17,1189,413],{}," turns context capture off entirely.",[14,1192,1193],{},[673,1194,801],{},[41,1196,1198],{"className":77,"code":1197,"language":79,"meta":46,"style":46},"context: {\n  env?: boolean        \u002F\u002F capture pid, hostname, NODE_ENV, default true\n  caller?: boolean     \u002F\u002F capture file\u002Fline\u002Fcolumn per log, default false\n  callerDepth?: number \u002F\u002F extra stack frames to skip for wrapper layers, default 0\n}\n",[17,1199,1200,1206,1218,1230,1243],{"__ignoreMap":46},[50,1201,1202,1204],{"class":52,"line":53},[50,1203,528],{"class":56},[50,1205,813],{"class":90},[50,1207,1208,1210,1212,1215],{"class":52,"line":100},[50,1209,1172],{"class":90},[50,1211,821],{"class":86},[50,1213,1214],{"class":90}," boolean        ",[50,1216,1217],{"class":218},"\u002F\u002F capture pid, hostname, NODE_ENV, default true\n",[50,1219,1220,1223,1225,1227],{"class":52,"line":107},[50,1221,1222],{"class":90},"  caller",[50,1224,821],{"class":86},[50,1226,885],{"class":90},[50,1228,1229],{"class":218},"\u002F\u002F capture file\u002Fline\u002Fcolumn per log, default false\n",[50,1231,1232,1235,1237,1240],{"class":52,"line":129},[50,1233,1234],{"class":90},"  callerDepth",[50,1236,821],{"class":86},[50,1238,1239],{"class":90}," number ",[50,1241,1242],{"class":218},"\u002F\u002F extra stack frames to skip for wrapper layers, default 0\n",[50,1244,1245],{"class":52,"line":134},[50,1246,944],{"class":90},[14,1248,1249,1252,1253,1256,1257,1262],{},[17,1250,1251],{},"caller"," reads ",[17,1254,1255],{},"Error.stack"," on every log, which adds a little overhead per call: use it in development or at low volume. ",[673,1258,1259],{},[17,1260,1261],{},"callerDepth"," is for when you wrap the Lumber client:",[41,1264,1266],{"className":77,"code":1265,"language":79,"meta":46,"style":46},"\u002F\u002F Your wrapper adds one frame to the stack\nfunction log(msg: string) {\n  lumber.info(msg)\n}\n\n\u002F\u002F Without callerDepth, context.code points to this wrapper, not your caller\nlumber.config({\n  context: { caller: true, callerDepth: 1 },\n})\n",[17,1267,1268,1273,1296,1306,1310,1314,1319,1327,1342],{"__ignoreMap":46},[50,1269,1270],{"class":52,"line":53},[50,1271,1272],{"class":218},"\u002F\u002F Your wrapper adds one frame to the stack\n",[50,1274,1275,1278,1281,1283,1287,1290,1293],{"class":52,"line":100},[50,1276,1277],{"class":86},"function",[50,1279,1280],{"class":56}," log",[50,1282,142],{"class":90},[50,1284,1286],{"class":1285},"s9osk","msg",[50,1288,1289],{"class":86},":",[50,1291,1292],{"class":119}," string",[50,1294,1295],{"class":90},") {\n",[50,1297,1298,1301,1303],{"class":52,"line":107},[50,1299,1300],{"class":90},"  lumber.",[50,1302,139],{"class":56},[50,1304,1305],{"class":90},"(msg)\n",[50,1307,1308],{"class":52,"line":129},[50,1309,944],{"class":90},[50,1311,1312],{"class":52,"line":134},[50,1313,104],{"emptyLinePlaceholder":103},[50,1315,1316],{"class":52,"line":156},[50,1317,1318],{"class":218},"\u002F\u002F Without callerDepth, context.code points to this wrapper, not your caller\n",[50,1320,1321,1323,1325],{"class":52,"line":183},[50,1322,110],{"class":90},[50,1324,113],{"class":56},[50,1326,1025],{"class":90},[50,1328,1329,1332,1334,1337,1340],{"class":52,"line":210},[50,1330,1331],{"class":90},"  context: { caller: ",[50,1333,795],{"class":119},[50,1335,1336],{"class":90},", callerDepth: ",[50,1338,1339],{"class":119},"1",[50,1341,630],{"class":90},[50,1343,1344],{"class":52,"line":215},[50,1345,286],{"class":90},[36,1347,758],{"id":1348},"delivery",[14,1350,1351,1352,1355,1356,1362],{},"Logging is non-blocking. Each ",[17,1353,1354],{},"lumber.*()"," call encrypts its log in-process and pushes it onto an internal queue, which sends one ",[22,1357,1359],{"href":1358},"\u002Fdocs\u002Fhttp-api",[17,1360,1361],{},"POST \u002Flogs"," per log in the background, and your code carries on at once.",[1364,1365,1366,1370,1376,1379,1382],"ul",{},[1367,1368,1369],"li",{},"Logs are sent one at a time, in the order they were logged. While a log is being retried, the logs behind it wait their turn.",[1367,1371,1372,1373,1375],{},"A network error, a 5xx response or no answer within ",[17,1374,748],{}," counts as a failed attempt. Each log gets up to 5 attempts, waiting 1, 2, 4, then 8 seconds between them, and is then dropped with a warning on the console.",[1367,1377,1378],{},"A log Lumber refuses (a 4xx, such as an invalid API key) is dropped at once with a warning, since sending it again won't help.",[1367,1380,1381],{},"When your team has sent its plan's logs for the month, Lumber answers 429: the SDK warns once and drops logs until the month turns, rather than queueing them.",[1367,1383,1384,1385,1387,1388,1392,1393,1396],{},"The queue is in memory, so logs still queued when the process exits are lost. Call ",[17,1386,652],{}," during graceful shutdown, as in the ",[22,1389,1391],{"href":1390},"#quick-start","quick start","; ",[17,1394,1395],{},"lumber.flush(5000)"," waits 5 seconds at most.",[36,1398,1400],{"id":1399},"serverless","Serverless",[14,1402,1403,1404,1407,1408,1411,1412,1289],{},"A serverless platform can freeze or stop a function as soon as it responds, with logs still queued. Keep the function running until they're sent with the platform's own hook, giving ",[17,1405,1406],{},"flush()"," a time limit so a slow answer can't hold the function to its maximum duration. On Vercel, with ",[17,1409,1410],{},"waitUntil"," from ",[17,1413,1414],{},"@vercel\u002Ffunctions",[41,1416,1418],{"className":77,"code":1417,"language":79,"meta":46,"style":46},"import { waitUntil } from '@vercel\u002Ffunctions'\n\nexport default async function handler(req, res) {\n  lumber.info('Handled request', { path: req.url })\n  res.send('ok')\n  waitUntil(lumber.flush(10_000))\n}\n",[17,1419,1420,1432,1436,1465,1479,1494,1512],{"__ignoreMap":46},[50,1421,1422,1424,1427,1429],{"class":52,"line":53},[50,1423,87],{"class":86},[50,1425,1426],{"class":90}," { waitUntil } ",[50,1428,94],{"class":86},[50,1430,1431],{"class":60}," '@vercel\u002Ffunctions'\n",[50,1433,1434],{"class":52,"line":100},[50,1435,104],{"emptyLinePlaceholder":103},[50,1437,1438,1441,1444,1447,1450,1453,1455,1458,1460,1463],{"class":52,"line":107},[50,1439,1440],{"class":86},"export",[50,1442,1443],{"class":86}," default",[50,1445,1446],{"class":86}," async",[50,1448,1449],{"class":86}," function",[50,1451,1452],{"class":56}," handler",[50,1454,142],{"class":90},[50,1456,1457],{"class":1285},"req",[50,1459,236],{"class":90},[50,1461,1462],{"class":1285},"res",[50,1464,1295],{"class":90},[50,1466,1467,1469,1471,1473,1476],{"class":52,"line":129},[50,1468,1300],{"class":90},[50,1470,139],{"class":56},[50,1472,142],{"class":90},[50,1474,1475],{"class":60},"'Handled request'",[50,1477,1478],{"class":90},", { path: req.url })\n",[50,1480,1481,1484,1487,1489,1492],{"class":52,"line":134},[50,1482,1483],{"class":90},"  res.",[50,1485,1486],{"class":56},"send",[50,1488,142],{"class":90},[50,1490,1491],{"class":60},"'ok'",[50,1493,280],{"class":90},[50,1495,1496,1499,1502,1504,1506,1509],{"class":52,"line":156},[50,1497,1498],{"class":56},"  waitUntil",[50,1500,1501],{"class":90},"(lumber.",[50,1503,260],{"class":56},[50,1505,142],{"class":90},[50,1507,1508],{"class":119},"10_000",[50,1510,1511],{"class":90},"))\n",[50,1513,1514],{"class":52,"line":183},[50,1515,944],{"class":90},[14,1517,1518,1519,1522],{},"Where there's no such hook (AWS Lambda), ",[17,1520,1521],{},"await lumber.flush(10_000)"," before returning.",[36,1524,1526],{"id":1525},"more-than-one-client","More than one client",[14,1528,1529,1530,1533],{},"For isolated clients (per-tenant logging, say), use ",[17,1531,1532],{},"LumberClient"," instead of the module's own:",[41,1535,1537],{"className":77,"code":1536,"language":79,"meta":46,"style":46},"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",[17,1538,1539,1550,1554,1573,1578,1582,1586,1606],{"__ignoreMap":46},[50,1540,1541,1543,1546,1548],{"class":52,"line":53},[50,1542,87],{"class":86},[50,1544,1545],{"class":90}," { LumberClient } ",[50,1547,94],{"class":86},[50,1549,97],{"class":60},[50,1551,1552],{"class":52,"line":100},[50,1553,104],{"emptyLinePlaceholder":103},[50,1555,1556,1559,1562,1565,1568,1571],{"class":52,"line":107},[50,1557,1558],{"class":86},"const",[50,1560,1561],{"class":119}," logger",[50,1563,1564],{"class":86}," =",[50,1566,1567],{"class":86}," new",[50,1569,1570],{"class":56}," LumberClient",[50,1572,1025],{"class":90},[50,1574,1575],{"class":52,"line":129},[50,1576,1577],{"class":90},"  apiKey: tenantApiKey,\n",[50,1579,1580],{"class":52,"line":134},[50,1581,286],{"class":90},[50,1583,1584],{"class":52,"line":156},[50,1585,104],{"emptyLinePlaceholder":103},[50,1587,1588,1591,1593,1595,1598,1601,1604],{"class":52,"line":183},[50,1589,1590],{"class":90},"logger.",[50,1592,139],{"class":56},[50,1594,142],{"class":90},[50,1596,1597],{"class":60},"'Tenant action'",[50,1599,1600],{"class":90},", { tenantId: ",[50,1602,1603],{"class":60},"'123'",[50,1605,126],{"class":90},[50,1607,1608,1611,1614,1616],{"class":52,"line":210},[50,1609,1610],{"class":86},"await",[50,1612,1613],{"class":90}," logger.",[50,1615,260],{"class":56},[50,1617,263],{"class":90},[14,1619,1620,1621,70],{},"It takes the same options as ",[17,1622,292],{},[36,1624,1626],{"id":1625},"source","Source",[14,1628,1629,1630,1636,1637,1641],{},"The SDK is open source: ",[22,1631,1635],{"href":1632,"rel":1633},"https:\u002F\u002Fgithub.com\u002Finfinitetoken\u002FLumber-Node",[1634],"nofollow","Lumber-Node"," on GitHub, published as ",[22,1638,19],{"href":1639,"rel":1640},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@infinitetoken\u002Flumber",[1634]," on npm. It's tested against the same known answers for encryption and the wire format as Lumber's other clients.",[1643,1644,1645],"style",{},"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 .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":46,"searchDepth":100,"depth":100,"links":1647},[1648,1649,1650,1651,1652,1664,1665,1666,1667],{"id":38,"depth":100,"text":39},{"id":73,"depth":100,"text":74},{"id":312,"depth":100,"text":313},{"id":425,"depth":100,"text":426},{"id":643,"depth":100,"text":543,"children":1653},[1654,1656,1658,1660,1662],{"id":661,"depth":107,"text":1655},"apiKey · string · required",{"id":711,"depth":107,"text":1657},"endpoint · string · optional",{"id":745,"depth":107,"text":1659},"timeout · number · optional",{"id":779,"depth":107,"text":1661},"console · boolean | ConsoleOptions · optional",{"id":1106,"depth":107,"text":1663},"context · boolean | ContextOptions · optional",{"id":1348,"depth":100,"text":758},{"id":1399,"depth":100,"text":1400},{"id":1525,"depth":100,"text":1526},{"id":1625,"depth":100,"text":1626},"Encrypted logging for Node.js with @infinitetoken\u002Flumber: install it, log with structured data, every lumber.config() option, delivery and serverless.","md",{},"\u002Fdocs\u002Fnode",{"title":5,"description":1668},"docs\u002F02.node","Sw3991rTdn1KwtX-4aRH7YbH4pWyvE3YGHsAMkoVEUg",[1676,1679],{"title":303,"path":1677,"stem":1678,"children":-1},"\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":1680,"path":1681,"stem":1682,"children":-1},"Python","\u002Fdocs\u002Fpython","docs\u002F03.python",1794332090903]