22namespace Buffer ;
33require_once ('vendor/autoload.php ' );
44
5- use Monolog \Logger ;
6- use Monolog \Handler \StreamHandler as MonologStreamHandler ;
7-
8- /*
9- Level of logs we use:
10-
11- This level require manual action to appear in Datadog Logs
12- Logger::DEBUG
13- Logger::INFO
14-
15- Everything at this level appears by default in Datadog Logs
16- Logger::WARNING
17- Logger::ERROR
18- Logger::CRITICAL
19- */
5+ use Monolog \Logger as Logger ;
6+ use Monolog \Handler \StreamHandler ;
207
218class BuffLog {
229
23- private static $ logger = null ;
24- private static $ currentVerbosity = Logger::WARNING ;
25- private static $ verbosityList = [
10+ protected static $ instance ;
11+ private static $ logger = null ;
12+ private static $ currentVerbosity = Logger::WARNING ;
13+ private static $ verbosityList = [
2614 "DEBUG " => Logger::DEBUG ,
2715 "INFO " => Logger::INFO ,
2816 "WARNING " => Logger::WARNING ,
2917 "ERROR " => Logger::ERROR ,
3018 "CRITICAL " => Logger::CRITICAL
3119 ];
3220
33- public static function debug ($ message , $ context = [])
34- {
35- self ::setVerbosity ();
36- if (self ::$ currentVerbosity > Logger::DEBUG ) {
37- return ;
38- }
21+ private static $ logOutputMethods = ['debug ' , 'info ' , 'notice ' , 'warning ' , 'error ' , 'critical ' ];
22+ private static $ extraAllowedMethods = ['getName ' , 'pushHandler ' , 'setHandlers ' , 'getHandlers ' , 'pushProcessor ' , 'getProcessors ' ];
3923
40- $ logOutput = self ::formatLog ($ message , Logger::DEBUG , $ context );
41- self ::getLogger ()->debug ($ logOutput );
42- }
24+ /**
25+ * Method to return the Monolog instance
26+ *
27+ * @return \Monolog\Logger
28+ */
29+ static public function getLogger ()
30+ {
31+ if (! self ::$ instance ) {
32+ self ::configureInstance ();
4333
44- public static function info ($ message , $ context = [])
45- {
46- self ::setVerbosity ();
47- if (self ::$ currentVerbosity > Logger::INFO ) {
48- return ;
4934 }
35+ return self ::$ instance ;
36+ }
5037
51- $ logOutput = self ::formatLog ($ message , Logger::INFO , $ context );
52- self ::getLogger ()->info ($ logOutput );
53- }
54-
55- public static function warning ($ message , $ context = [])
38+ protected static function configureInstance ()
39+ {
40+ // @TODO: We could potentially use the Kubernetes downward API to
41+ // define the logger name. This will make it easier for developers
42+ // to read and friendlier to identify where come the logs at a glance
43+ $ logger = new Logger ('php-bufflog ' );
44+ $ handler = new StreamHandler ('php://stdout ' );
45+ $ handler ->setFormatter ( new \Monolog \Formatter \JsonFormatter () );
46+ $ logger ->pushHandler ($ handler );
47+ self ::$ instance = $ logger ;
48+ }
49+
50+ // This will be called when a static method in the class doesn't exists
51+ public static function __callStatic ($ methodName , $ args )
5652 {
57- self ::setVerbosity ();
58- if (self ::$ currentVerbosity > Logger::WARNING ) {
59- return ;
60- }
53+ if (method_exists (self ::getLogger (), $ methodName )) {
6154
62- $ logOutput = self ::formatLog ($ message , Logger::WARNING , $ context );
63- self ::getLogger ()->warn ($ logOutput );
64- }
55+ if (in_array ($ methodName , array_merge (self ::$ logOutputMethods , self ::$ extraAllowedMethods ))) {
6556
66- public static function error ($ message , $ context = [])
67- {
68- self ::setVerbosity ();
69- if (self ::$ currentVerbosity > Logger::ERROR ) {
70- return ;
71- }
57+ if (in_array ($ methodName , self ::$ logOutputMethods )) {
7258
73- $ logOutput = self ::formatLog ($ message , Logger::ERROR , $ context );
74- self ::getLogger ()->error ($ logOutput );
75- }
59+ // @TODO: need to make sure we "output" only the correct level of log
60+ // old version looked like:
61+ // self::setVerbosity();
62+ // if (self::$currentVerbosity > Logger::WARNING) {
63+ // return;
64+ // }
7665
77- // @TODO: That one might could also create an alert in Datadog?
78- public static function critical ($ message , $ context = [])
79- {
80- self ::setVerbosity ();
81- $ logOutput = self ::formatLog ($ message , Logger::CRITICAL , $ context );
82- self ::getLogger ()->critical ($ logOutput );
83- }
66+ self ::enrichLog ();
67+ }
68+ // Where the magic happen. We "proxy" functions name with arguments to the Monolog instance
69+ return call_user_func_array (array (self ::getLogger (), $ methodName ), $ args );
8470
85- private function formatLog ($ message , $ level , $ context = [])
86- {
87- // Add traces information to logs to be able correlate with APM
88- $ ddTraceSpan = \DDTrace \GlobalTracer::get ()->getActiveSpan ();
89- $ context ['dd ' ] = [
90- "trace_id " => $ ddTraceSpan ->getTraceId (),
91- "span_id " => $ ddTraceSpan ->getSpanId ()
92- ];
93-
94- $ output = [
95- "message " => $ message ,
96- "level " => $ level ,
97- "datetime " => date (\DateTime::ATOM ),
98- // we could use timestamp if we need ms precision (but it isn't readable) https://docs.datadoghq.com/logs/processing/#reserved-attributes
99- // 'timestamp' => round(microtime(true) * 1000),
100- "context " => $ context
101- ];
102-
103- try {
104- $ output = json_encode ($ output , JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
105- } catch (Exception $ e ) {
106- error_log ("can't json_encode your message " );
107- }
71+ } else {
10872
109- return $ output ;
73+ error_log ("BuffLog:: $ methodName() is not supported yet. Add it to the BuffLog whitelist to allow it " );
74+ }
75+ } else {
76+ error_log ("BuffLog:: $ methodName() does not exist " );
77+ }
11078 }
11179
112- private static function createLogger ()
80+ private function enrichLog ()
11381 {
114- // @TODO: We could potentially use the Kubernetes downward API to
115- // define the logger name. This will make it easier for developers
116- // to read and friendlier to identify where come the logs at a glance
117- self ::$ logger = new Logger ('php-bufflog ' );
118- $ handler = new MonologStreamHandler ('php://stdout ' );
119-
120- self ::$ logger ->pushHandler ($ handler );
121- return self ::$ logger ;
82+ // This should probably implemented as a Monolog Processor
83+ // https://github.com/Seldaek/monolog/tree/master/src/Monolog/Processor
84+ self ::getLogger ()->pushProcessor (function ($ record ) {
85+
86+ // We should grab any Buffer information useful when available
87+ // Need to check with the Core team: accountID / userID / profileID
88+ // $user = Buffer/Core::getCurrentUser();
89+ // That should look like:
90+ // $record['context']['user'] = array(
91+ // 'accountID' => $user->getAccountID(),
92+ // 'userID' => $user->getUserID(),
93+ // 'profileID' => $user->getProfileID()
94+ // );
95+
96+ // Add traces information to logs to be able correlate with APM
97+ $ ddTraceSpan = \DDTrace \GlobalTracer::get ()->getActiveSpan ();
98+ $ record ['context ' ]['dd ' ] = [
99+ "trace_id " => $ ddTraceSpan ->getTraceId (),
100+ "span_id " => $ ddTraceSpan ->getSpanId ()
101+ ];
102+ return $ record ;
103+ });
122104 }
123105
124106 private static function setVerbosity ()
@@ -129,14 +111,4 @@ private static function setVerbosity()
129111 }
130112 }
131113
132- public static function getLogger ()
133- {
134- if (!isset (self ::$ logger )) {
135- echo "Initializing logger \n" ;
136- self ::createLogger ();
137- }
138-
139- return self ::$ logger ;
140- }
141-
142114}
0 commit comments