Skip to content
File

Blob: src/workerd/util/sentry.h

cpp100 lines
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#pragma once
6// Some utilities related to logging, particularly with respect to how Cloudflare's edge runtime's
7// Sentry integration will end up treating the logs.
8 
9#include <kj/common.h>
10#include <kj/debug.h>
11#include <kj/exception.h>
12#include <kj/string.h>
13#include <kj/time.h>
14 
15#include <cstdint>
16 
17namespace workerd {
18 
19// Log out an exception with context but without frills. This macro excludes any variadic arguments
20// from the macro so that we do not accidentally make a more granular fingerprint. It also will only
21// take a `context` argument that is known at compile time (via constexpr assignment).
22#define LOG_EXCEPTION(context, exception) \
23 [&](const kj::Exception& e) { \
24 constexpr auto sentryErrorContext = context; \
25 KJ_LOG(ERROR, e, sentryErrorContext); \
26 }(exception)
27 
28#define ACTOR_STORAGE_OP_PREFIX "; actorStorageOp = "
29 
30inline bool isInterestingException(const kj::Exception& e) {
31 return e.getType() != kj::Exception::Type::DISCONNECTED &&
32 e.getType() != kj::Exception::Type::OVERLOADED;
33}
34 
35#define LOG_NOSENTRY(severity, ...) KJ_LOG(severity, "NOSENTRY " __VA_ARGS__);
36 
37#define LOG_IF_INTERESTING(exception, severity, ...) \
38 if (!::workerd::isInterestingException(exception)) { \
39 LOG_NOSENTRY(severity, __VA_ARGS__); \
40 } else { \
41 KJ_LOG(severity, __VA_ARGS__); \
42 }
43 
44// Log this to Sentry once ever per process. Typically will be better to use LOG_PERIODICALLY.
45#define LOG_ONCE(severity, ...) \
46 do { \
47 static bool logOnce KJ_UNUSED = [&]() { \
48 KJ_LOG(severity, __VA_ARGS__); \
49 return true; \
50 }(); \
51 } while (0)
52 
53// Log this to Sentry once ever per process. Typically will be better to use LOG_WARNING_PERIODICALLY.
54#define LOG_WARNING_ONCE(...) LOG_ONCE(WARNING, __VA_ARGS__);
55 
56// Log this to Sentry once ever per process. Typically will be better to use LOG_ERROR_PERIODICALLY.
57#define LOG_ERROR_ONCE(...) LOG_ONCE(ERROR, __VA_ARGS__);
58 
59// Slightly more expensive than LOG_ONCE. Avoid putting into a hot path (e.g. within a loop)
60// where an overhead of ~hundreds of nanoseconds per evaluation to retrieve the current time would
61// be prohibitive.
62#define LOG_PERIODICALLY(severity, ...) \
63 do { \
64 static kj::TimePoint KJ_UNIQUE_NAME(lastLogged) = kj::origin<kj::TimePoint>() - 1 * kj::HOURS; \
65 const auto KJ_UNIQUE_NAME(now) = kj::systemCoarseMonotonicClock().now(); \
66 const auto KJ_UNIQUE_NAME(elapsed) = KJ_UNIQUE_NAME(now) - KJ_UNIQUE_NAME(lastLogged); \
67 if (KJ_UNLIKELY(KJ_UNIQUE_NAME(elapsed) >= 1 * kj::HOURS)) { \
68 KJ_UNIQUE_NAME(lastLogged) = KJ_UNIQUE_NAME(now); \
69 KJ_LOG(severity, __VA_ARGS__); \
70 } \
71 } while (0)
72 
73// Slightly more expensive than LOG_WARNING_ONCE. Avoid putting into a hot path (e.g. within a loop)
74// where an overhead of ~hundreds of nanoseconds per evaluation to retrieve the current time would
75// be prohibitive.
76#define LOG_WARNING_PERIODICALLY(...) LOG_PERIODICALLY(WARNING, __VA_ARGS__);
77 
78// Slightly more expensive than LOG_ERROR_ONCE. Avoid putting into a hot path (e.g. within a loop)
79// where an overhead of ~hundreds of nanoseconds per evaluation to retrieve the current time would
80// be prohibitive.
81#define LOG_ERROR_PERIODICALLY(...) LOG_PERIODICALLY(ERROR, __VA_ARGS__);
82 
83// The DEBUG_FATAL_RELEASE_LOG macros is for assertions that should definitely break in tests but
84// are not worth breaking production over. Instead, it logs the assertion message to sentry so that
85// we can notice the event. If your code requires that an assertion is true for safety (e.g.
86// checking if a value is not null), this is not the macro for you.
87#ifdef KJ_DEBUG
88#define DEBUG_FATAL_RELEASE_LOG(severity, ...) ([&]() noexcept { KJ_FAIL_ASSERT(__VA_ARGS__); })()
89#else
90#define DEBUG_FATAL_RELEASE_LOG(severity, ...) \
91 do { \
92 static bool logOnce KJ_UNUSED = [&]() { \
93 KJ_LOG(severity, __VA_ARGS__); \
94 return true; \
95 }(); \
96 } while (0)
97#endif
98 
99} // namespace workerd