Skip to content
File

Blob: src/workerd/server/json-logger.c++

4.4 KB
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include "json-logger.h"
6 
7#include <workerd/server/log-schema.capnp.h>
8 
9#include <capnp/compat/json.h>
10#include <capnp/message.h>
11#include <kj/debug.h>
12#include <kj/io.h>
13#include <kj/main.h>
14#include <kj/miniposix.h>
15#include <kj/string.h>
16 
17namespace workerd::server {
18 
19log_schema::LogEntry::LogLevel severityToLogLevel(kj::LogSeverity severity) {
20 switch (severity) {
21 case kj::LogSeverity::INFO:
22 return log_schema::LogEntry::LogLevel::INFO;
23 case kj::LogSeverity::WARNING:
24 return log_schema::LogEntry::LogLevel::WARNING;
25 case kj::LogSeverity::ERROR:
26 return log_schema::LogEntry::LogLevel::ERROR;
27 case kj::LogSeverity::FATAL:
28 return log_schema::LogEntry::LogLevel::FATAL;
29 case kj::LogSeverity::DBG:
30 return log_schema::LogEntry::LogLevel::DEBUG_;
31 }
32}
33 
34kj::String buildJsonLogMessage(
35 kj::LogSeverity severity, const char* file, int line, int contextDepth, kj::StringPtr text) {
36 capnp::MallocMessageBuilder message;
37 auto logEntry = message.initRoot<log_schema::LogEntry>();
38 
39 logEntry.setTimestamp(
40 (kj::systemPreciseCalendarClock().now() - kj::UNIX_EPOCH) / kj::MILLISECONDS);
41 
42 logEntry.setLevel(severityToLogLevel(severity));
43 
44 auto location = kj::str(file, ":", line);
45 logEntry.setSource(location);
46 
47 logEntry.setMessage(text);
48 
49 if (contextDepth > 0) {
50 logEntry.setContextDepth(static_cast<uint32_t>(contextDepth));
51 }
52 
53 capnp::JsonCodec codec;
54 codec.handleByAnnotation<log_schema::LogEntry>();
55 codec.setPrettyPrint(false); // Compact JSON for logs
56 return codec.encode(logEntry);
57}
58 
59void JsonLogger::logMessage(
60 kj::LogSeverity severity, const char* file, int line, int contextDepth, kj::String&& text) {
61 // Prevent infinite recursion if logging code itself logs
62 if (loggingInProgress) {
63 return;
64 }
65 loggingInProgress = true;
66 KJ_DEFER(loggingInProgress = false);
67 
68 auto json = buildJsonLogMessage(severity, file, line, contextDepth, text);
69 
70 // Write directly to stdout with no buffering.
71 kj::FdOutputStream(STDOUT_FILENO).write({json.asBytes(), "\n"_kj.asBytes()});
72}
73 
74kj::Function<void(kj::Function<void()>)> JsonLogger::getThreadInitializer() {
75 auto nextInit = next.getThreadInitializer();
76 
77 return [nextInit = kj::mv(nextInit)](kj::Function<void()> func) mutable {
78 nextInit([&]() {
79 JsonLogger logger;
80 
81 // Make sure func is destroyed before the context is destroyed.
82 auto ownFunc = kj::mv(func);
83 ownFunc();
84 });
85 };
86}
87 
88// =======================================================================================
89// StructuredLoggingProcessContext implementation
90 
91StructuredLoggingProcessContext::StructuredLoggingProcessContext(kj::StringPtr programName)
92 : topLevelContext(programName) {}
93 
94void StructuredLoggingProcessContext::enableStructuredLogging() {
95 useStructuredLogging = true;
96 jsonLogger.emplace();
97}
98 
99kj::StringPtr StructuredLoggingProcessContext::getProgramName() {
100 return topLevelContext.getProgramName();
101}
102 
103void StructuredLoggingProcessContext::exit() {
104 topLevelContext.exit();
105}
106 
107void StructuredLoggingProcessContext::warning(kj::StringPtr message) const {
108 if (useStructuredLogging) {
109 auto json = buildJsonLogMessage(kj::LogSeverity::WARNING, __FILE__, __LINE__, 0, message);
110 topLevelContext.warning(json);
111 } else {
112 topLevelContext.warning(message);
113 }
114}
115 
116void StructuredLoggingProcessContext::error(kj::StringPtr message) const {
117 if (useStructuredLogging) {
118 auto json = buildJsonLogMessage(kj::LogSeverity::ERROR, __FILE__, __LINE__, 0, message);
119 topLevelContext.error(json);
120 } else {
121 topLevelContext.error(message);
122 }
123}
124 
125void StructuredLoggingProcessContext::exitError(kj::StringPtr message) {
126 if (useStructuredLogging) {
127 auto json = buildJsonLogMessage(kj::LogSeverity::ERROR, __FILE__, __LINE__, 0, message);
128 topLevelContext.exitError(json);
129 } else {
130 topLevelContext.exitError(message);
131 }
132}
133 
134void StructuredLoggingProcessContext::exitInfo(kj::StringPtr message) {
135 if (useStructuredLogging) {
136 auto json = buildJsonLogMessage(kj::LogSeverity::INFO, __FILE__, __LINE__, 0, message);
137 topLevelContext.exitInfo(json);
138 } else {
139 topLevelContext.exitInfo(message);
140 }
141}
142 
143void StructuredLoggingProcessContext::increaseLoggingVerbosity() {
144 topLevelContext.increaseLoggingVerbosity();
145}
146 
147} // namespace workerd::server