[{"data":1,"prerenderedAt":1876},["ShallowReactive",2],{"docs-page-\u002Fdocs\u002Fdotnet":3,"docs-surround-\u002Fdocs\u002Fdotnet":1867},{"id":4,"title":5,"body":6,"description":1860,"extension":1861,"meta":1862,"navigation":156,"path":1863,"seo":1864,"stem":1865,"__hash__":1866},"docs\u002Fdocs\u002F06.dotnet.md","C# and .NET",{"type":7,"value":8,"toc":1847},"minimark",[9,13,31,36,39,90,118,121,125,302,305,318,324,340,346,516,531,541,637,652,703,710,770,776,820,824,840,996,1003,1032,1044,1110,1120,1124,1239,1246,1250,1253,1375,1411,1414,1613,1638,1642,1652,1674,1681,1685,1688,1806,1816,1820,1843],[10,11,5],"h1",{"id":12},"c-and-net",[14,15,16,20,21,26,27,30],"p",{},[17,18,19],"code",{},"InfiniteToken.Lumber"," is Lumber's .NET client. It encrypts every log with ",[22,23,25],"a",{"href":24},"\u002Fdocs\u002Fencryption","AES-256-GCM"," before it leaves your process, has no dependencies beyond the base library, and plugs into ",[17,28,29],{},"ILogger"," (ASP.NET Core, worker services, Azure Functions) and Serilog.",[32,33,35],"h2",{"id":34},"install","Install",[14,37,38],{},"There are three packages on NuGet. Add the one for how you log; each brings the client with it.",[40,41,42,55],"table",{},[43,44,45],"thead",{},[46,47,48,52],"tr",{},[49,50,51],"th",{},"Package",[49,53,54],{},"For",[56,57,58,68,80],"tbody",{},[46,59,60,65],{},[61,62,63],"td",{},[17,64,19],{},[61,66,67],{},"The client on its own",[46,69,70,75],{},[61,71,72],{},[17,73,74],{},"InfiniteToken.Lumber.Extensions.Logging",[61,76,77,79],{},[17,78,29],{},": ASP.NET Core, the generic host, worker services",[46,81,82,87],{},[61,83,84],{},[17,85,86],{},"InfiniteToken.Lumber.Serilog",[61,88,89],{},"A Serilog sink",[91,92,97],"pre",{"className":93,"code":94,"language":95,"meta":96,"style":96},"language-bash shiki shiki-themes github-dark","dotnet add package InfiniteToken.Lumber\n","bash","",[17,98,99],{"__ignoreMap":96},[100,101,104,108,112,115],"span",{"class":102,"line":103},"line",1,[100,105,107],{"class":106},"svObZ","dotnet",[100,109,111],{"class":110},"sU2Wk"," add",[100,113,114],{"class":110}," package",[100,116,117],{"class":110}," InfiniteToken.Lumber\n",[14,119,120],{},".NET 8 or later, .NET Framework 4.6.2 or later, or anything else that runs .NET Standard 2.0.",[32,122,124],{"id":123},"quick-start","Quick start",[91,126,130],{"className":127,"code":128,"language":129,"meta":96,"style":96},"language-csharp shiki shiki-themes github-dark","using InfiniteToken.Lumber;\n\nusing var lumber = new LumberClient(Environment.GetEnvironmentVariable(\"LUMBER_API_KEY\"));\n\nlumber.Info(\"Server started\", new { port = 8080 });\nlumber.Warn(\"Rate limit approaching\", new { RequestId = \"req-123\", userId = 456 });\nlumber.Error(exception, \"Payment failed\", new { TraceId = \"trace-abc\", orderId = 789 });\n","csharp",[17,131,132,151,158,193,198,231,266],{"__ignoreMap":96},[100,133,134,138,141,145,148],{"class":102,"line":103},[100,135,137],{"class":136},"snl16","using",[100,139,140],{"class":106}," InfiniteToken",[100,142,144],{"class":143},"s95oV",".",[100,146,147],{"class":106},"Lumber",[100,149,150],{"class":143},";\n",[100,152,154],{"class":102,"line":153},2,[100,155,157],{"emptyLinePlaceholder":156},true,"\n",[100,159,161,163,166,169,172,175,178,181,184,187,190],{"class":102,"line":160},3,[100,162,137],{"class":136},[100,164,165],{"class":136}," var",[100,167,168],{"class":106}," lumber",[100,170,171],{"class":136}," =",[100,173,174],{"class":136}," new",[100,176,177],{"class":106}," LumberClient",[100,179,180],{"class":143},"(Environment.",[100,182,183],{"class":106},"GetEnvironmentVariable",[100,185,186],{"class":143},"(",[100,188,189],{"class":110},"\"LUMBER_API_KEY\"",[100,191,192],{"class":143},"));\n",[100,194,196],{"class":102,"line":195},4,[100,197,157],{"emptyLinePlaceholder":156},[100,199,201,204,207,209,212,215,218,221,224,228],{"class":102,"line":200},5,[100,202,203],{"class":143},"lumber.",[100,205,206],{"class":106},"Info",[100,208,186],{"class":143},[100,210,211],{"class":110},"\"Server started\"",[100,213,214],{"class":143},", ",[100,216,217],{"class":136},"new",[100,219,220],{"class":143}," { port ",[100,222,223],{"class":136},"=",[100,225,227],{"class":226},"sDLfK"," 8080",[100,229,230],{"class":143}," });\n",[100,232,234,236,239,241,244,246,248,251,253,256,259,261,264],{"class":102,"line":233},6,[100,235,203],{"class":143},[100,237,238],{"class":106},"Warn",[100,240,186],{"class":143},[100,242,243],{"class":110},"\"Rate limit approaching\"",[100,245,214],{"class":143},[100,247,217],{"class":136},[100,249,250],{"class":143}," { RequestId ",[100,252,223],{"class":136},[100,254,255],{"class":110}," \"req-123\"",[100,257,258],{"class":143},", userId ",[100,260,223],{"class":136},[100,262,263],{"class":226}," 456",[100,265,230],{"class":143},[100,267,269,271,274,277,280,282,284,287,289,292,295,297,300],{"class":102,"line":268},7,[100,270,203],{"class":143},[100,272,273],{"class":106},"Error",[100,275,276],{"class":143},"(exception, ",[100,278,279],{"class":110},"\"Payment failed\"",[100,281,214],{"class":143},[100,283,217],{"class":136},[100,285,286],{"class":143}," { TraceId ",[100,288,223],{"class":136},[100,290,291],{"class":110}," \"trace-abc\"",[100,293,294],{"class":143},", orderId ",[100,296,223],{"class":136},[100,298,299],{"class":226}," 789",[100,301,230],{"class":143},[14,303,304],{},"Make one client and share it: it's safe to use from any thread. Logs are sent in the background, so logging never waits on the network. Disposing the client sends the logs still queued, waiting 5 seconds at most, and so does the process exiting.",[14,306,307,308,312,313,317],{},"The API key comes from the Lumber app (see ",[22,309,311],{"href":310},"\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,314,316],{"href":315},"\u002Fdocs\u002Fapi-keys#keep-the-key-on-your-servers","why",").",[32,319,321,322],{"id":320},"aspnet-core-and-ilogger","ASP.NET Core and ",[17,323,29],{},[91,325,327],{"className":93,"code":326,"language":95,"meta":96,"style":96},"dotnet add package InfiniteToken.Lumber.Extensions.Logging\n",[17,328,329],{"__ignoreMap":96},[100,330,331,333,335,337],{"class":102,"line":103},[100,332,107],{"class":106},[100,334,111],{"class":110},[100,336,114],{"class":110},[100,338,339],{"class":110}," InfiniteToken.Lumber.Extensions.Logging\n",[14,341,342,343,345],{},"It's a logging provider, so everything that already logs through ",[17,344,29],{},", your code and the framework's, goes to Lumber too:",[91,347,349],{"className":127,"code":348,"language":129,"meta":96,"style":96},"var builder = WebApplication.CreateBuilder(args);\n\nbuilder.Logging.AddLumber(options => options.Environment = builder.Environment.EnvironmentName);\n\nvar app = builder.Build();\napp.MapPost(\"\u002Fpay\", (Order order, ILogger\u003CProgram> logger) =>\n{\n    logger.LogInformation(\"Charging {OrderId} for {Total}\", order.Id, order.Total);\n    return Results.Ok();\n});\napp.Run();\n",[17,350,351,370,374,398,402,420,464,469,486,500,506],{"__ignoreMap":96},[100,352,353,356,359,361,364,367],{"class":102,"line":103},[100,354,355],{"class":136},"var",[100,357,358],{"class":106}," builder",[100,360,171],{"class":136},[100,362,363],{"class":143}," WebApplication.",[100,365,366],{"class":106},"CreateBuilder",[100,368,369],{"class":143},"(args);\n",[100,371,372],{"class":102,"line":153},[100,373,157],{"emptyLinePlaceholder":156},[100,375,376,379,382,384,387,390,393,395],{"class":102,"line":160},[100,377,378],{"class":143},"builder.Logging.",[100,380,381],{"class":106},"AddLumber",[100,383,186],{"class":143},[100,385,386],{"class":106},"options",[100,388,389],{"class":136}," =>",[100,391,392],{"class":143}," options.Environment ",[100,394,223],{"class":136},[100,396,397],{"class":143}," builder.Environment.EnvironmentName);\n",[100,399,400],{"class":102,"line":195},[100,401,157],{"emptyLinePlaceholder":156},[100,403,404,406,409,411,414,417],{"class":102,"line":200},[100,405,355],{"class":136},[100,407,408],{"class":106}," app",[100,410,171],{"class":136},[100,412,413],{"class":143}," builder.",[100,415,416],{"class":106},"Build",[100,418,419],{"class":143},"();\n",[100,421,422,425,428,430,433,436,439,442,444,446,449,452,455,458,461],{"class":102,"line":233},[100,423,424],{"class":143},"app.",[100,426,427],{"class":106},"MapPost",[100,429,186],{"class":143},[100,431,432],{"class":110},"\"\u002Fpay\"",[100,434,435],{"class":143},", (",[100,437,438],{"class":106},"Order",[100,440,441],{"class":106}," order",[100,443,214],{"class":143},[100,445,29],{"class":106},[100,447,448],{"class":143},"\u003C",[100,450,451],{"class":106},"Program",[100,453,454],{"class":143},"> ",[100,456,457],{"class":106},"logger",[100,459,460],{"class":143},") ",[100,462,463],{"class":136},"=>\n",[100,465,466],{"class":102,"line":268},[100,467,468],{"class":143},"{\n",[100,470,472,475,478,480,483],{"class":102,"line":471},8,[100,473,474],{"class":143},"    logger.",[100,476,477],{"class":106},"LogInformation",[100,479,186],{"class":143},[100,481,482],{"class":110},"\"Charging {OrderId} for {Total}\"",[100,484,485],{"class":143},", order.Id, order.Total);\n",[100,487,489,492,495,498],{"class":102,"line":488},9,[100,490,491],{"class":136},"    return",[100,493,494],{"class":143}," Results.",[100,496,497],{"class":106},"Ok",[100,499,419],{"class":143},[100,501,503],{"class":102,"line":502},10,[100,504,505],{"class":143},"});\n",[100,507,509,511,514],{"class":102,"line":508},11,[100,510,424],{"class":143},[100,512,513],{"class":106},"Run",[100,515,419],{"class":143},[14,517,518,519,522,523,526,527,530],{},"The API key comes from the ",[17,520,521],{},"LUMBER_API_KEY"," environment variable or configuration value (so ",[17,524,525],{},"dotnet user-secrets set LUMBER_API_KEY \u003Ckey>"," works in development), or from ",[17,528,529],{},"Logging:Lumber:ApiKey",". With no key anywhere, the provider warns once and sends nothing, so a machine without one keeps logging to the console as usual.",[14,532,533,534,536,537,540],{},"Which levels go to Lumber is set like any provider's, under its alias, ",[17,535,147],{},". In ",[17,538,539],{},"appsettings.json",":",[91,542,546],{"className":543,"code":544,"language":545,"meta":96,"style":96},"language-json shiki shiki-themes github-dark","{\n  \"Logging\": {\n    \"LogLevel\": { \"Default\": \"Information\" },\n    \"Lumber\": {\n      \"LogLevel\": { \"Default\": \"Warning\", \"MyApp\": \"Information\" },\n      \"Environment\": \"Production\"\n    }\n  }\n}\n","json",[17,547,548,552,560,580,587,612,622,627,632],{"__ignoreMap":96},[100,549,550],{"class":102,"line":103},[100,551,468],{"class":143},[100,553,554,557],{"class":102,"line":153},[100,555,556],{"class":226},"  \"Logging\"",[100,558,559],{"class":143},": {\n",[100,561,562,565,568,571,574,577],{"class":102,"line":160},[100,563,564],{"class":226},"    \"LogLevel\"",[100,566,567],{"class":143},": { ",[100,569,570],{"class":226},"\"Default\"",[100,572,573],{"class":143},": ",[100,575,576],{"class":110},"\"Information\"",[100,578,579],{"class":143}," },\n",[100,581,582,585],{"class":102,"line":195},[100,583,584],{"class":226},"    \"Lumber\"",[100,586,559],{"class":143},[100,588,589,592,594,596,598,601,603,606,608,610],{"class":102,"line":200},[100,590,591],{"class":226},"      \"LogLevel\"",[100,593,567],{"class":143},[100,595,570],{"class":226},[100,597,573],{"class":143},[100,599,600],{"class":110},"\"Warning\"",[100,602,214],{"class":143},[100,604,605],{"class":226},"\"MyApp\"",[100,607,573],{"class":143},[100,609,576],{"class":110},[100,611,579],{"class":143},[100,613,614,617,619],{"class":102,"line":233},[100,615,616],{"class":226},"      \"Environment\"",[100,618,573],{"class":143},[100,620,621],{"class":110},"\"Production\"\n",[100,623,624],{"class":102,"line":268},[100,625,626],{"class":143},"    }\n",[100,628,629],{"class":102,"line":471},[100,630,631],{"class":143},"  }\n",[100,633,634],{"class":102,"line":488},[100,635,636],{"class":143},"}\n",[14,638,639,640,643,644,647,648,651],{},"Each log is sent at the matching level (",[17,641,642],{},"Trace"," and ",[17,645,646],{},"Debug"," are DEBUG, ",[17,649,650],{},"Critical"," is FATAL), with the formatted message.",[653,654,655,666,684],"ul",{},[656,657,658,659,214,662,665],"li",{},"The template's values (",[17,660,661],{},"OrderId",[17,663,664],{},"Total",") go into the log's metadata, and so do the values of the logger's scopes.",[656,667,668,669,643,672,675,676,679,680,683],{},"In ASP.NET Core, each request's scope supplies its ",[17,670,671],{},"RequestId",[17,673,674],{},"TraceId",", which become the log's own request and trace IDs; ",[17,677,678],{},"RequestPath"," goes into metadata. ",[17,681,682],{},"IncludeScopes = false"," leaves scopes out.",[656,685,686,687,689,690,643,693,696,697,643,700,144],{},"The logger's category goes into the context as ",[17,688,457],{},", an exception into metadata as ",[17,691,692],{},"exception",[17,694,695],{},"stackTrace",", and an event ID as ",[17,698,699],{},"eventId",[17,701,702],{},"eventName",[14,704,705,706,709],{},"Worker services and console apps on the generic host add it the same way, with ",[17,707,708],{},"builder.Logging.AddLumber()",". Without a host, make a factory and dispose it before exiting:",[91,711,713],{"className":127,"code":712,"language":129,"meta":96,"style":96},"using var factory = LoggerFactory.Create(logging => logging.AddLumber());\nvar logger = factory.CreateLogger(\"Importer\");\n",[17,714,715,747],{"__ignoreMap":96},[100,716,717,719,721,724,726,729,732,734,737,739,742,744],{"class":102,"line":103},[100,718,137],{"class":136},[100,720,165],{"class":136},[100,722,723],{"class":106}," factory",[100,725,171],{"class":136},[100,727,728],{"class":143}," LoggerFactory.",[100,730,731],{"class":106},"Create",[100,733,186],{"class":143},[100,735,736],{"class":106},"logging",[100,738,389],{"class":136},[100,740,741],{"class":143}," logging.",[100,743,381],{"class":106},[100,745,746],{"class":143},"());\n",[100,748,749,751,754,756,759,762,764,767],{"class":102,"line":153},[100,750,355],{"class":136},[100,752,753],{"class":106}," logger",[100,755,171],{"class":136},[100,757,758],{"class":143}," factory.",[100,760,761],{"class":106},"CreateLogger",[100,763,186],{"class":143},[100,765,766],{"class":110},"\"Importer\"",[100,768,769],{"class":143},");\n",[14,771,772,773,775],{},"To log through ",[17,774,29],{}," and call the client yourself with one client, pass it in, and dispose it yourself:",[91,777,779],{"className":127,"code":778,"language":129,"meta":96,"style":96},"var lumber = new LumberClient(builder.Configuration[\"LUMBER_API_KEY\"]);\nbuilder.Services.AddSingleton(lumber);\nbuilder.Logging.AddLumber(lumber);\n",[17,780,781,801,812],{"__ignoreMap":96},[100,782,783,785,787,789,791,793,796,798],{"class":102,"line":103},[100,784,355],{"class":136},[100,786,168],{"class":106},[100,788,171],{"class":136},[100,790,174],{"class":136},[100,792,177],{"class":106},[100,794,795],{"class":143},"(builder.Configuration[",[100,797,189],{"class":110},[100,799,800],{"class":143},"]);\n",[100,802,803,806,809],{"class":102,"line":153},[100,804,805],{"class":143},"builder.Services.",[100,807,808],{"class":106},"AddSingleton",[100,810,811],{"class":143},"(lumber);\n",[100,813,814,816,818],{"class":102,"line":160},[100,815,378],{"class":143},[100,817,381],{"class":106},[100,819,811],{"class":143},[32,821,823],{"id":822},"serilog","Serilog",[91,825,827],{"className":93,"code":826,"language":95,"meta":96,"style":96},"dotnet add package InfiniteToken.Lumber.Serilog\n",[17,828,829],{"__ignoreMap":96},[100,830,831,833,835,837],{"class":102,"line":103},[100,832,107],{"class":106},[100,834,111],{"class":110},[100,836,114],{"class":110},[100,838,839],{"class":110}," InfiniteToken.Lumber.Serilog\n",[91,841,843],{"className":127,"code":842,"language":129,"meta":96,"style":96},"using Serilog;\n\nLog.Logger = new LoggerConfiguration()\n    .WriteTo.Console()\n    .WriteTo.Lumber(Environment.GetEnvironmentVariable(\"LUMBER_API_KEY\"), environment: \"Production\")\n    .CreateLogger();\n\nLog.Information(\"Report ready for {Customer} in {Elapsed} ms\", \"acme\", 2400);\nLog.ForContext(\"RequestId\", \"req-123\").Error(exception, \"Payment failed for {@Order}\", order);\n\nLog.CloseAndFlush(); \u002F\u002F sends the logs still queued\n",[17,844,845,854,858,873,883,911,920,924,949,978,982],{"__ignoreMap":96},[100,846,847,849,852],{"class":102,"line":103},[100,848,137],{"class":136},[100,850,851],{"class":106}," Serilog",[100,853,150],{"class":143},[100,855,856],{"class":102,"line":153},[100,857,157],{"emptyLinePlaceholder":156},[100,859,860,863,865,867,870],{"class":102,"line":160},[100,861,862],{"class":143},"Log.Logger ",[100,864,223],{"class":136},[100,866,174],{"class":136},[100,868,869],{"class":106}," LoggerConfiguration",[100,871,872],{"class":143},"()\n",[100,874,875,878,881],{"class":102,"line":195},[100,876,877],{"class":143},"    .WriteTo.",[100,879,880],{"class":106},"Console",[100,882,872],{"class":143},[100,884,885,887,889,891,893,895,897,900,903,905,908],{"class":102,"line":200},[100,886,877],{"class":143},[100,888,147],{"class":106},[100,890,180],{"class":143},[100,892,183],{"class":106},[100,894,186],{"class":143},[100,896,189],{"class":110},[100,898,899],{"class":143},"), ",[100,901,902],{"class":106},"environment",[100,904,573],{"class":143},[100,906,907],{"class":110},"\"Production\"",[100,909,910],{"class":143},")\n",[100,912,913,916,918],{"class":102,"line":233},[100,914,915],{"class":143},"    .",[100,917,761],{"class":106},[100,919,419],{"class":143},[100,921,922],{"class":102,"line":268},[100,923,157],{"emptyLinePlaceholder":156},[100,925,926,929,932,934,937,939,942,944,947],{"class":102,"line":471},[100,927,928],{"class":143},"Log.",[100,930,931],{"class":106},"Information",[100,933,186],{"class":143},[100,935,936],{"class":110},"\"Report ready for {Customer} in {Elapsed} ms\"",[100,938,214],{"class":143},[100,940,941],{"class":110},"\"acme\"",[100,943,214],{"class":143},[100,945,946],{"class":226},"2400",[100,948,769],{"class":143},[100,950,951,953,956,958,961,963,966,968,970,972,975],{"class":102,"line":488},[100,952,928],{"class":143},[100,954,955],{"class":106},"ForContext",[100,957,186],{"class":143},[100,959,960],{"class":110},"\"RequestId\"",[100,962,214],{"class":143},[100,964,965],{"class":110},"\"req-123\"",[100,967,317],{"class":143},[100,969,273],{"class":106},[100,971,276],{"class":143},[100,973,974],{"class":110},"\"Payment failed for {@Order}\"",[100,976,977],{"class":143},", order);\n",[100,979,980],{"class":102,"line":502},[100,981,157],{"emptyLinePlaceholder":156},[100,983,984,986,989,992],{"class":102,"line":508},[100,985,928],{"class":143},[100,987,988],{"class":106},"CloseAndFlush",[100,990,991],{"class":143},"(); ",[100,993,995],{"class":994},"sAwPA","\u002F\u002F sends the logs still queued\n",[14,997,998,999,1002],{},"Each event is sent at the matching level (",[17,1000,1001],{},"Verbose"," is DEBUG), its message rendered as Serilog's console sink renders it.",[653,1004,1005,1008,1025],{},[656,1006,1007],{},"Its properties go into the log's metadata, structures as nested objects.",[656,1009,1010,643,1012,1014,1015,1018,1019,1022,1023,144],{},[17,1011,671],{},[17,1013,674],{}," are the log's own request and trace IDs, and ",[17,1016,1017],{},"SourceContext"," (from ",[17,1020,1021],{},"ForContext\u003CT>()",") goes into its context as ",[17,1024,457],{},[656,1026,1027,1028,1031],{},"When an ",[17,1029,1030],{},"Activity"," is running, its trace and span IDs are sent too.",[14,1033,1034,1037,1038,1040,1041,1043],{},[17,1035,1036],{},"apiKey"," defaults to the ",[17,1039,521],{}," environment variable, and every argument can come from ",[17,1042,539],{}," through Serilog.Settings.Configuration:",[91,1045,1047],{"className":543,"code":1046,"language":545,"meta":96,"style":96},"{\n  \"Serilog\": {\n    \"WriteTo\": [{ \"Name\": \"Lumber\", \"Args\": { \"environment\": \"Production\", \"restrictedToMinimumLevel\": \"Information\" } }]\n  }\n}\n",[17,1048,1049,1053,1060,1102,1106],{"__ignoreMap":96},[100,1050,1051],{"class":102,"line":103},[100,1052,468],{"class":143},[100,1054,1055,1058],{"class":102,"line":153},[100,1056,1057],{"class":226},"  \"Serilog\"",[100,1059,559],{"class":143},[100,1061,1062,1065,1068,1071,1073,1076,1078,1081,1083,1086,1088,1090,1092,1095,1097,1099],{"class":102,"line":160},[100,1063,1064],{"class":226},"    \"WriteTo\"",[100,1066,1067],{"class":143},": [{ ",[100,1069,1070],{"class":226},"\"Name\"",[100,1072,573],{"class":143},[100,1074,1075],{"class":110},"\"Lumber\"",[100,1077,214],{"class":143},[100,1079,1080],{"class":226},"\"Args\"",[100,1082,567],{"class":143},[100,1084,1085],{"class":226},"\"environment\"",[100,1087,573],{"class":143},[100,1089,907],{"class":110},[100,1091,214],{"class":143},[100,1093,1094],{"class":226},"\"restrictedToMinimumLevel\"",[100,1096,573],{"class":143},[100,1098,576],{"class":110},[100,1100,1101],{"class":143}," } }]\n",[100,1103,1104],{"class":102,"line":195},[100,1105,631],{"class":143},[100,1107,1108],{"class":102,"line":200},[100,1109,636],{"class":143},[14,1111,1112,1115,1116,1119],{},[17,1113,1114],{},"WriteTo.Lumber(options => ...)"," takes every option below, and ",[17,1117,1118],{},"WriteTo.Lumber(lumber)"," a client you share with your own calls.",[32,1121,1123],{"id":1122},"log-methods","Log methods",[40,1125,1126,1136],{},[43,1127,1128],{},[46,1129,1130,1133],{},[49,1131,1132],{},"Method",[49,1134,1135],{},"Level",[56,1137,1138,1148,1158,1168,1178,1188,1201,1222],{},[46,1139,1140,1145],{},[61,1141,1142],{},[17,1143,1144],{},"Debug(message, data)",[61,1146,1147],{},"DEBUG",[46,1149,1150,1155],{},[61,1151,1152],{},[17,1153,1154],{},"Info(message, data)",[61,1156,1157],{},"INFO",[46,1159,1160,1165],{},[61,1161,1162],{},[17,1163,1164],{},"Warn(message, data)",[61,1166,1167],{},"WARN",[46,1169,1170,1175],{},[61,1171,1172],{},[17,1173,1174],{},"Error(message, data)",[61,1176,1177],{},"ERROR",[46,1179,1180,1185],{},[61,1181,1182],{},[17,1183,1184],{},"Fatal(message, data)",[61,1186,1187],{},"FATAL. It doesn't end the process",[46,1189,1190,1195],{},[61,1191,1192],{},[17,1193,1194],{},"Log(level, message, data)",[61,1196,1197,1198],{},"Any level, as a ",[17,1199,1200],{},"LumberLevel",[46,1202,1203,1211],{},[61,1204,1205,214,1208],{},[17,1206,1207],{},"Flush(timeout)",[17,1209,1210],{},"FlushAsync(timeout, cancellationToken)",[61,1212,1213,1214,1217,1218,1221],{},"Waits for queued logs to send: ",[17,1215,1216],{},"false"," if ",[17,1219,1220],{},"timeout"," passed first",[46,1223,1224,1232],{},[61,1225,1226,214,1229],{},[17,1227,1228],{},"Dispose()",[17,1230,1231],{},"DisposeAsync()",[61,1233,1234,1235,1238],{},"Sends what's queued, waiting ",[17,1236,1237],{},"ShutdownTimeout"," at most, and stops",[14,1240,1241,1242,1245],{},"Each log method also takes an exception first: ",[17,1243,1244],{},"lumber.Error(exception, \"Payment failed\", data)",". Logging never throws and never blocks.",[32,1247,1249],{"id":1248},"data","Data",[14,1251,1252],{},"The second argument is the log's data: an anonymous object, a dictionary, or any object, whose properties become the log's fields.",[91,1254,1256],{"className":127,"code":1255,"language":129,"meta":96,"style":96},"lumber.Info(\"User signed in\", new\n{\n    RequestId = \"req-abc\",                \u002F\u002F the log's request ID (requestId works too)\n    TraceId = \"trace-xyz\",                \u002F\u002F the log's trace ID (traceId works too)\n    context = new { region = \"us-east\" }, \u002F\u002F merged into its context\n    metadata = new { plan = \"pro\" },      \u002F\u002F merged into its metadata\n    userId = 123,                         \u002F\u002F any other property goes into metadata\n});\n",[17,1257,1258,1274,1278,1294,1309,1332,1355,1371],{"__ignoreMap":96},[100,1259,1260,1262,1264,1266,1269,1271],{"class":102,"line":103},[100,1261,203],{"class":143},[100,1263,206],{"class":106},[100,1265,186],{"class":143},[100,1267,1268],{"class":110},"\"User signed in\"",[100,1270,214],{"class":143},[100,1272,1273],{"class":136},"new\n",[100,1275,1276],{"class":102,"line":153},[100,1277,468],{"class":143},[100,1279,1280,1283,1285,1288,1291],{"class":102,"line":160},[100,1281,1282],{"class":143},"    RequestId ",[100,1284,223],{"class":136},[100,1286,1287],{"class":110}," \"req-abc\"",[100,1289,1290],{"class":143},",                ",[100,1292,1293],{"class":994},"\u002F\u002F the log's request ID (requestId works too)\n",[100,1295,1296,1299,1301,1304,1306],{"class":102,"line":195},[100,1297,1298],{"class":143},"    TraceId ",[100,1300,223],{"class":136},[100,1302,1303],{"class":110}," \"trace-xyz\"",[100,1305,1290],{"class":143},[100,1307,1308],{"class":994},"\u002F\u002F the log's trace ID (traceId works too)\n",[100,1310,1311,1314,1316,1318,1321,1323,1326,1329],{"class":102,"line":200},[100,1312,1313],{"class":143},"    context ",[100,1315,223],{"class":136},[100,1317,174],{"class":136},[100,1319,1320],{"class":143}," { region ",[100,1322,223],{"class":136},[100,1324,1325],{"class":110}," \"us-east\"",[100,1327,1328],{"class":143}," }, ",[100,1330,1331],{"class":994},"\u002F\u002F merged into its context\n",[100,1333,1334,1337,1339,1341,1344,1346,1349,1352],{"class":102,"line":233},[100,1335,1336],{"class":143},"    metadata ",[100,1338,223],{"class":136},[100,1340,174],{"class":136},[100,1342,1343],{"class":143}," { plan ",[100,1345,223],{"class":136},[100,1347,1348],{"class":110}," \"pro\"",[100,1350,1351],{"class":143}," },      ",[100,1353,1354],{"class":994},"\u002F\u002F merged into its metadata\n",[100,1356,1357,1360,1362,1365,1368],{"class":102,"line":268},[100,1358,1359],{"class":143},"    userId ",[100,1361,223],{"class":136},[100,1363,1364],{"class":226}," 123",[100,1366,1367],{"class":143},",                         ",[100,1369,1370],{"class":994},"\u002F\u002F any other property goes into metadata\n",[100,1372,1373],{"class":102,"line":471},[100,1374,505],{"class":143},[14,1376,1377,1378,1381,1382,1384,1385,1387,1388,1391,1392,1395,1396,214,1399,1402,1403,1406,1407,1410],{},"An explicit ",[17,1379,1380],{},"metadata"," wins over a colliding property. An exception goes into metadata as ",[17,1383,692],{}," (its type and message) and ",[17,1386,695],{}," (the whole of ",[17,1389,1390],{},"ToString()",", inner exceptions included). Values JSON has no type for are sent as text (a ",[17,1393,1394],{},"DateTime"," as ISO 8601, an enum as its name, a ",[17,1397,1398],{},"Guid",[17,1400,1401],{},"TimeSpan"," or ",[17,1404,1405],{},"Uri"," as you'd write it), a NaN as null, and a structure that contains itself as ",[17,1408,1409],{},"\"[Circular]\"",". Everything, the message included, is encrypted.",[32,1412,1413],{"id":386},"Options",[91,1415,1417],{"className":127,"code":1416,"language":129,"meta":96,"style":96},"var lumber = new LumberClient(new LumberOptions\n{\n    ApiKey = Environment.GetEnvironmentVariable(\"LUMBER_API_KEY\"),\n    Environment = \"Production\",                 \u002F\u002F sent as `env` in every log's context\n    IncludeCaller = true,                       \u002F\u002F send the file, line and method that logged\n    AutoContext = false,                        \u002F\u002F don't send the process id, hostname and environment\n    Timeout = TimeSpan.FromSeconds(10),         \u002F\u002F how long to wait for Lumber to answer one log\n    MaxQueue = 10_000,                          \u002F\u002F logs waiting to send before new ones are dropped\n    ShutdownTimeout = TimeSpan.FromSeconds(5),  \u002F\u002F how long queued logs get on Dispose or exit\n    HttpClient = httpClient,                    \u002F\u002F your own HttpClient, for a proxy or custom TLS\n    OnWarning = message => Console.WriteLine(message), \u002F\u002F the client's warnings, in place of standard error\n});\n",[17,1418,1419,1438,1442,1461,1477,1493,1509,1533,1549,1571,1584,1608],{"__ignoreMap":96},[100,1420,1421,1423,1425,1427,1429,1431,1433,1435],{"class":102,"line":103},[100,1422,355],{"class":136},[100,1424,168],{"class":106},[100,1426,171],{"class":136},[100,1428,174],{"class":136},[100,1430,177],{"class":106},[100,1432,186],{"class":143},[100,1434,217],{"class":136},[100,1436,1437],{"class":106}," LumberOptions\n",[100,1439,1440],{"class":102,"line":153},[100,1441,468],{"class":143},[100,1443,1444,1447,1449,1452,1454,1456,1458],{"class":102,"line":160},[100,1445,1446],{"class":143},"    ApiKey ",[100,1448,223],{"class":136},[100,1450,1451],{"class":143}," Environment.",[100,1453,183],{"class":106},[100,1455,186],{"class":143},[100,1457,189],{"class":110},[100,1459,1460],{"class":143},"),\n",[100,1462,1463,1466,1468,1471,1474],{"class":102,"line":195},[100,1464,1465],{"class":143},"    Environment ",[100,1467,223],{"class":136},[100,1469,1470],{"class":110}," \"Production\"",[100,1472,1473],{"class":143},",                 ",[100,1475,1476],{"class":994},"\u002F\u002F sent as `env` in every log's context\n",[100,1478,1479,1482,1484,1487,1490],{"class":102,"line":200},[100,1480,1481],{"class":143},"    IncludeCaller ",[100,1483,223],{"class":136},[100,1485,1486],{"class":226}," true",[100,1488,1489],{"class":143},",                       ",[100,1491,1492],{"class":994},"\u002F\u002F send the file, line and method that logged\n",[100,1494,1495,1498,1500,1503,1506],{"class":102,"line":233},[100,1496,1497],{"class":143},"    AutoContext ",[100,1499,223],{"class":136},[100,1501,1502],{"class":226}," false",[100,1504,1505],{"class":143},",                        ",[100,1507,1508],{"class":994},"\u002F\u002F don't send the process id, hostname and environment\n",[100,1510,1511,1514,1516,1519,1522,1524,1527,1530],{"class":102,"line":268},[100,1512,1513],{"class":143},"    Timeout ",[100,1515,223],{"class":136},[100,1517,1518],{"class":143}," TimeSpan.",[100,1520,1521],{"class":106},"FromSeconds",[100,1523,186],{"class":143},[100,1525,1526],{"class":226},"10",[100,1528,1529],{"class":143},"),         ",[100,1531,1532],{"class":994},"\u002F\u002F how long to wait for Lumber to answer one log\n",[100,1534,1535,1538,1540,1543,1546],{"class":102,"line":471},[100,1536,1537],{"class":143},"    MaxQueue ",[100,1539,223],{"class":136},[100,1541,1542],{"class":226}," 10_000",[100,1544,1545],{"class":143},",                          ",[100,1547,1548],{"class":994},"\u002F\u002F logs waiting to send before new ones are dropped\n",[100,1550,1551,1554,1556,1558,1560,1562,1565,1568],{"class":102,"line":488},[100,1552,1553],{"class":143},"    ShutdownTimeout ",[100,1555,223],{"class":136},[100,1557,1518],{"class":143},[100,1559,1521],{"class":106},[100,1561,186],{"class":143},[100,1563,1564],{"class":226},"5",[100,1566,1567],{"class":143},"),  ",[100,1569,1570],{"class":994},"\u002F\u002F how long queued logs get on Dispose or exit\n",[100,1572,1573,1576,1578,1581],{"class":102,"line":502},[100,1574,1575],{"class":143},"    HttpClient ",[100,1577,223],{"class":136},[100,1579,1580],{"class":143}," httpClient,                    ",[100,1582,1583],{"class":994},"\u002F\u002F your own HttpClient, for a proxy or custom TLS\n",[100,1585,1586,1589,1591,1594,1596,1599,1602,1605],{"class":102,"line":508},[100,1587,1588],{"class":143},"    OnWarning ",[100,1590,223],{"class":136},[100,1592,1593],{"class":106}," message",[100,1595,389],{"class":136},[100,1597,1598],{"class":143}," Console.",[100,1600,1601],{"class":106},"WriteLine",[100,1603,1604],{"class":143},"(message), ",[100,1606,1607],{"class":994},"\u002F\u002F the client's warnings, in place of standard error\n",[100,1609,1611],{"class":102,"line":1610},12,[100,1612,505],{"class":143},[14,1614,1615,1616,1619,1620,1402,1623,1626,1627,1629,1630,1633,1634,1637],{},"Unless set, ",[17,1617,1618],{},"Environment"," is the ",[17,1621,1622],{},"ASPNETCORE_ENVIRONMENT",[17,1624,1625],{},"DOTNET_ENVIRONMENT"," environment variable, when either is set. The ",[17,1628,29],{}," provider takes the same options, plus ",[17,1631,1632],{},"IncludeScopes",", and can read them all from ",[17,1635,1636],{},"Logging:Lumber"," in configuration.",[32,1639,1641],{"id":1640},"delivery","Delivery",[14,1643,1644,1645,1651],{},"Logs are sent in order, one ",[22,1646,1648],{"href":1647},"\u002Fdocs\u002Fhttp-api",[17,1649,1650],{},"POST \u002Flogs"," per log, over connections kept open between logs.",[653,1653,1654,1661,1664,1667],{},[656,1655,1656,1657,1660],{},"A network error, a 5xx or no answer within ",[17,1658,1659],{},"Timeout"," counts as a failed attempt. Each log gets 5 attempts, waiting 1, 2, 4, then 8 seconds between them, and is then dropped with a warning.",[656,1662,1663],{},"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.",[656,1665,1666],{},"When your team has sent its plan's logs for the month, Lumber answers 429: the client warns once and drops logs until the month turns.",[656,1668,1669,1670,1673],{},"While Lumber can't be reached, up to ",[17,1671,1672],{},"MaxQueue"," logs wait; past that, new ones are dropped.",[14,1675,1676,1677,1680],{},"Warnings go to standard error unless you pass ",[17,1678,1679],{},"OnWarning",". Never send them back into Lumber through a logger, or one failure becomes many.",[32,1682,1684],{"id":1683},"serverless","Serverless",[14,1686,1687],{},"A serverless platform can freeze an instance as soon as it returns, with logs still queued. On AWS Lambda, wait for them before returning, with a time limit so a slow answer can't hold the function to its maximum duration:",[91,1689,1691],{"className":127,"code":1690,"language":129,"meta":96,"style":96},"public async Task\u003CAPIGatewayProxyResponse> Handle(APIGatewayProxyRequest request, ILambdaContext context)\n{\n    lumber.Info(\"Handled request\", new { path = request.Path });\n    await lumber.FlushAsync(TimeSpan.FromSeconds(10));\n    return new APIGatewayProxyResponse { StatusCode = 200 };\n}\n",[17,1692,1693,1732,1736,1760,1782,1802],{"__ignoreMap":96},[100,1694,1695,1698,1701,1704,1706,1709,1711,1714,1716,1719,1722,1724,1727,1730],{"class":102,"line":103},[100,1696,1697],{"class":136},"public",[100,1699,1700],{"class":136}," async",[100,1702,1703],{"class":106}," Task",[100,1705,448],{"class":143},[100,1707,1708],{"class":106},"APIGatewayProxyResponse",[100,1710,454],{"class":143},[100,1712,1713],{"class":106},"Handle",[100,1715,186],{"class":143},[100,1717,1718],{"class":106},"APIGatewayProxyRequest",[100,1720,1721],{"class":106}," request",[100,1723,214],{"class":143},[100,1725,1726],{"class":106},"ILambdaContext",[100,1728,1729],{"class":106}," context",[100,1731,910],{"class":143},[100,1733,1734],{"class":102,"line":153},[100,1735,468],{"class":143},[100,1737,1738,1741,1743,1745,1748,1750,1752,1755,1757],{"class":102,"line":160},[100,1739,1740],{"class":143},"    lumber.",[100,1742,206],{"class":106},[100,1744,186],{"class":143},[100,1746,1747],{"class":110},"\"Handled request\"",[100,1749,214],{"class":143},[100,1751,217],{"class":136},[100,1753,1754],{"class":143}," { path ",[100,1756,223],{"class":136},[100,1758,1759],{"class":143}," request.Path });\n",[100,1761,1762,1765,1768,1771,1774,1776,1778,1780],{"class":102,"line":195},[100,1763,1764],{"class":136},"    await",[100,1766,1767],{"class":143}," lumber.",[100,1769,1770],{"class":106},"FlushAsync",[100,1772,1773],{"class":143},"(TimeSpan.",[100,1775,1521],{"class":106},[100,1777,186],{"class":143},[100,1779,1526],{"class":226},[100,1781,192],{"class":143},[100,1783,1784,1786,1788,1791,1794,1796,1799],{"class":102,"line":200},[100,1785,491],{"class":136},[100,1787,174],{"class":136},[100,1789,1790],{"class":106}," APIGatewayProxyResponse",[100,1792,1793],{"class":143}," { StatusCode ",[100,1795,223],{"class":136},[100,1797,1798],{"class":226}," 200",[100,1800,1801],{"class":143}," };\n",[100,1803,1804],{"class":102,"line":233},[100,1805,636],{"class":143},[14,1807,1808,1809,1811,1812,1815],{},"Make the client once, outside the handler, so warm invocations reuse it and its connection. Logging through ",[17,1810,29],{}," there, share a client with ",[17,1813,1814],{},"AddLumber(lumber)"," so you can flush it. Azure Functions' isolated worker runs between invocations like any other process, so its logs send in the background, and the host flushes them when it shuts down.",[32,1817,1819],{"id":1818},"source","Source",[14,1821,1822,1823,1829,1830,214,1834,643,1838,1842],{},"The client is open source: ",[22,1824,1828],{"href":1825,"rel":1826},"https:\u002F\u002Fgithub.com\u002Finfinitetoken\u002FLumber-DotNet",[1827],"nofollow","Lumber-DotNet"," on GitHub, published as ",[22,1831,19],{"href":1832,"rel":1833},"https:\u002F\u002Fwww.nuget.org\u002Fpackages\u002FInfiniteToken.Lumber",[1827],[22,1835,74],{"href":1836,"rel":1837},"https:\u002F\u002Fwww.nuget.org\u002Fpackages\u002FInfiniteToken.Lumber.Extensions.Logging",[1827],[22,1839,86],{"href":1840,"rel":1841},"https:\u002F\u002Fwww.nuget.org\u002Fpackages\u002FInfiniteToken.Lumber.Serilog",[1827]," on NuGet. It's tested against the same known answers for encryption and the wire format as Lumber's other clients.",[1844,1845,1846],"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}",{"title":96,"searchDepth":153,"depth":153,"links":1848},[1849,1850,1851,1853,1854,1855,1856,1857,1858,1859],{"id":34,"depth":153,"text":35},{"id":123,"depth":153,"text":124},{"id":320,"depth":153,"text":1852},"ASP.NET Core and ILogger",{"id":822,"depth":153,"text":823},{"id":1122,"depth":153,"text":1123},{"id":1248,"depth":153,"text":1249},{"id":386,"depth":153,"text":1413},{"id":1640,"depth":153,"text":1641},{"id":1683,"depth":153,"text":1684},{"id":1818,"depth":153,"text":1819},"Encrypted logging for C# and .NET with InfiniteToken.Lumber: install it, send logs from ILogger, ASP.NET Core or Serilog, and every option.","md",{},"\u002Fdocs\u002Fdotnet",{"title":5,"description":1860},"docs\u002F06.dotnet","LAcS79hZ_oh3yMR5PmIQ0A5oHrpfAJ5jvaQNKEBNpx0",[1868,1872],{"title":1869,"path":1870,"stem":1871,"children":-1},"Java and Kotlin","\u002Fdocs\u002Fjava","docs\u002F05.java",{"title":1873,"path":1874,"stem":1875,"children":-1},"PHP","\u002Fdocs\u002Fphp","docs\u002F07.php",1794332090955]