-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBuffLog.php
More file actions
147 lines (120 loc) · 5.89 KB
/
BuffLog.php
File metadata and controls
147 lines (120 loc) · 5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?php
namespace Buffer\BuffLog;
use Monolog\Logger as Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Level as LogLevel;
class BuffLog {
protected static $instance;
private static $logger = null;
// default verbosity starting at this level
private static $verbosityLevel = LogLevel::Notice;
// verbosity can be changed with setting this env var
public static $logLevelEnvVar = "LOG_LEVEL";
// we can use strtolower(Logger::getLevels()) instead
private static $logOutputMethods = ['debug', 'info', 'notice', 'warning', 'error', 'critical'];
private static $extraAllowedMethods = ['getName', 'pushHandler', 'setHandlers', 'getHandlers', 'pushProcessor', 'getProcessors', 'getLevels'];
/**
* Method to return the Monolog instance
*
* @return \Monolog\Logger
*/
static public function getLogger()
{
if (! self::$instance) {
self::configureInstance();
}
return self::$instance;
}
protected static function configureInstance()
{
// Check if DDTrace functions are available and warn if not
if (!function_exists("\DDTrace\logs_correlation_trace_id")) {
// local envs don't need tracing
if (getenv("ENVIRONMENT") !== "local") {
echo json_encode([
"message" => "Can't find DDTrace functions. Did you install the Datadog APM tracer extension? It will allow you to have logs enriched with traces making troubleshooting easier. If you run a cli mode service (such as a worker), did you set the DD_TRACE_CLI_ENABLED env variable?",
"level" => 300,
"level_name" => "WARNING",
"context" => ["bufflog_error" => "no_tracer"]
]);
}
}
// @TODO: We could potentially use the Kubernetes downward API to
// define the logger name. This will make it easier for developers
// to read and friendlier to identify where come the logs at a glance
$logger = new Logger('php-bufflog');
$logLevelFromEnv = getenv(self::$logLevelEnvVar);
if ($logLevelFromEnv) {
// only if the level exists, we change the verbosity level
if (in_array($logLevelFromEnv, LogLevel::VALUES) || in_array($logLevelFromEnv, LogLevel::NAMES)) {
self::$verbosityLevel = $logLevelFromEnv;
} else {
error_log(self::$logLevelEnvVar . "={$logLevelFromEnv} verbosity level does not exists. Please use: " . implode(', ', array_keys(LogLevel::VALUES)));
}
}
$handler = new StreamHandler('php://stdout', self::$verbosityLevel);
$handler->setFormatter( new \Monolog\Formatter\JsonFormatter() );
$logger->pushHandler($handler);
// We should probably implement this as a Monolog Processor
// https://github.com/Seldaek/monolog/tree/master/src/Monolog/Processor
$logger->pushProcessor(function ($record) {
// We should grab any Buffer information useful when available
// Need to check with the Core team: accountID / userID / profileID
// $user = Buffer/Core::getCurrentUser();
// That should look like:
// $record['context']['user'] = array(
// 'accountID' => $user->getAccountID(),
// 'userID' => $user->getUserID(),
// 'profileID' => $user->getProfileID()
// );
// Add traces information to correlate logs with APM
if (function_exists('\DDTrace\logs_correlation_trace_id') && function_exists('\dd_trace_peek_span_id')) {
try {
$record->extra['dd'] = [
'trace_id' => \DDTrace\logs_correlation_trace_id(),
'span_id' => \dd_trace_peek_span_id()
];
} catch (\Exception $e) {
// no-op
}
}
return $record;
});
self::$instance = $logger;
}
// This will be called when a static method in the class doesn't exists
public static function __callStatic($methodName, $args)
{
if (method_exists(self::getLogger(), $methodName)) {
if (in_array($methodName, array_merge(self::$logOutputMethods, self::$extraAllowedMethods))) {
if (in_array($methodName, self::$logOutputMethods)) {
if (self::checkLogParametersType($args)) {
// Where the magic happen. We "proxy" functions name with arguments to the Monolog instance
return call_user_func_array(array(self::getLogger(), $methodName), $args);
}
}
} else {
self::getLogger()->warning("BuffLog::$methodName() is not supported yet. Add it to the BuffLog whitelist to allow it", ["bufflog_error" => "method_not_supported"]);
}
} else {
self::getLogger()->warning("BuffLog::$methodName() method does not exist", ["bufflog_error" => "method_not_exist"]);
}
return false;
}
private static function checkLogParametersType($args)
{
if (count($args) > 2) {
self::getLogger()->warning("BuffLog: Malformed logs: Too many parameters", ["bufflog_error" => "incorrect_parameters", "args" => $args]);
return false;
}
if (isset($args[0]) && !is_string($args[0])) {
self::getLogger()->warning("BuffLog: Malformed logs: First parameter must be a string", ["args" => $args, "bufflog_error" => "incorrect_parameters"]);
return false;
}
if (isset($args[1]) && !is_array($args[1])) {
self::getLogger()->warning("BuffLog: Malformed logs: Second parameter must be an array", ["args" => $args, "bufflog_error" => "incorrect_parameters"]);
return false;
}
return true;
}
}