Roxen.git
/
server
/
etc
/
modules
/
Logger.pmod
version
»
Context lines:
10
20
40
80
file
none
3
Roxen.git/server/etc/modules/Logger.pmod:1:
//! Your average base class for loggers. This one outputs by default //! to stderr using the Roxen werror() method.
-
class
BaseLogger
{
+
class
BaseJSONLogger
{
+
constant BUNYAN_VERSION = 0;
+
object parent_logger;
-
mapping defaults = ([
+
string logger_name = "unknown";
+
string hostname = (functionp(System.gethostname) && System.gethostname()) || "unknown";
+
int pid = System.getpid();
+
+
mapping
|function
defaults = ([
"level" : INFO, ]); enum { TRACE = 10, DBG = 20, INFO = 30, WARN = 40, ERROR = 50, FATAL = 60 }; int default_log_level = INFO;
-
+
int drop_messages_below = 0;
//! Override to allow early bailout in @[log()] if noone is //! listening. @[log()] will bail out if this method returns 0.
-
int should_log() {
+
int should_log(
string|mapping msg
) {
+
int level = (stringp(msg) && default_log_level) || msg->level || default_log_level;
+
if (level < drop_messages_below) return 0;
+
if (parent_logger)
-
return parent_logger->should_log();
+
return parent_logger->should_log(
msg
);
return 1; } //! Send the actual log to listeners. //! Override this method to change where log is sent and how it is encoded. protected void do_log(mapping data) { if (!data->level) { data = data | ([]); // Make sure we don't modify the original mapping! data->level = default_log_level; } string res = Standards.JSON.encode(data); }
-
+
+
// Generate timestamps in ISO8601 extended format for Bunyan compatibility.
+
string get_bunyan_timestamp() {
+
array(int) tod = System.gettimeofday();
+
mapping gt = gmtime(tod[0]);
+
string ret = sprintf("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
+
1900 + gt->year, 1+gt->mon, gt->mday,
+
gt->hour, gt->min, gt->sec,
+
tod[1]/1000);
+
+
return ret;
+
}
+
//! Override this method to implement custom merging of data into //! messages. //! //! The default logic is to just merge the default //! mapping into the message. If more dynamic defaults (such as //! current time etc) is needed, overriding this method is your best //! bet. protected mapping merge_defaults(mapping msg) {
-
return
defaults | msg;
+
mapping
ret = ((functionp(
defaults
)
&& defaults())
|
|
defaults) |
msg;
+
+
// Now we ensure fields expected by Bunyan exists
+
ret->v = BUNYAN_VERSION;
+
ret->name = ret->name || logger_name;
+
ret->hostname = hostname;
+
ret->pid = pid;
+
ret->time = get_bunyan_timestamp();
+
ret->msg = ret->msg || "";
+
return ret;
} //! Log an entry using this logger. //! //! If data is a string, it will be converted into a mapping using //! the key "msg" for the message. //! //! Overriding this method should not be needed. void log(mapping|string data) { // Check for early bailout
-
if (!should_log()) {
+
if (!should_log(
data
)) {
return; } if (stringp(data)) { data = ([ "msg" : data ]); } mapping log_entry = merge_defaults(data); if (parent_logger && functionp(parent_logger->log)) { parent_logger->log(log_entry); } else { do_log(log_entry); } }
-
+
void set_name(void|string name) {
+
logger_name = name || "unknown";
+
}
+
+
void set_defaults(void|function|mapping new_defaults) {
+
defaults = new_defaults || ([]);
+
}
+
//! Default parameter mapping and a parent logger object. //! //! The @[parent_logger] object is used to pass any log messages //! injected into this logger up the chain. By default, this logger //! does not log at it's own level if a parent logger is given. Instead, //! it will simply add its defaults and pass the complete log entry up //! to the parent which is then responsible for handling the actual logging.
-
void create(void|mapping|function defaults, void|object parent_logger) {
-
this
_
program::
defaults
= defaults ||
(
[]
);
+
void create(void|
string logger_name, void|
mapping|function defaults, void|object parent_logger) {
+
set
_defaults
(
defaults);
this_program::parent_logger = parent_logger;
-
+
set_name(logger_name);
}
-
this_program child(void|mapping|function defaults) {
-
this_program new_logger = object_program(this)(defaults, this);
+
this_program child(
string logger_name,
void|mapping|function defaults) {
+
logger_name = combine_path_unix(
this_program
::logger_name,
logger_name);
+
this_program
new_logger = object_program(this)(
logger_name,
defaults, this);
return new_logger; } } // Output to all listeners on a Unix socket somewhere. // // Note: Any object using this class should have unbind_all() called // before the end of its life cycle to avoid leaving socket files on // disk. destroy() will also take care of this, but isn't always // called on cleanup. class SocketLogger {
-
inherit Logger.
BaseLogger
;
+
inherit Logger.
BaseJSONLogger
;
multiset(object) listeners = (<>); array(object) ports; mapping(string:object) ports_by_path = ([]); class LogClient { Stdio.File sock; Stdio.Buffer output = Stdio.Buffer(4096); // Write data to the log buffer (output) and set up the write
Roxen.git/server/etc/modules/Logger.pmod:156:
} void create(Stdio.File f) { sock = f; f->set_id(this); f->set_buffer_mode(Stdio.Buffer(1024), Stdio.Buffer(4096)); // Arbitrary buffer length - may need to be adjusted. f->set_nonblocking(read_cb, 0, close_cb); } };
-
int should_log() {
+
int should_log(
string|mapping msg
) {
// If noone is listening there is no point in doing any work.
-
return sizeof(listeners);
+
return
::should_log(msg) &&
sizeof(listeners);
} void do_log(mapping entry) { string res = Standards.JSON.encode(entry); Stdio.Buffer tmp = Stdio.Buffer(res); tmp->add("\n"); // Add a newline so that the reciever can parse data based on lines. sizeof(listeners) && indices(listeners)->write(tmp); } void accept_cb(object port) {
Roxen.git/server/etc/modules/Logger.pmod:251:
return 0; } // Unbind all bound sockets by this logger void unbind_all() { foreach(ports_by_path; string path; object port) { unbind(port); } }
-
void
create
(
void|mapping
defaults,
void|object
parent_
logger, void|
string
socket_path
) {
-
::
create
(defaults,
parent
_logger
)
;
+
//
+
// We override the child
(
)
method
because
children should not have
+
// their own listening sockets - they should just relay messages to
+
// us...
+
//
+
BaseJSONLogger child(string logger
_
name
, void|
mapping|function
defaults
) {
+
logger_name = combine_path_unix(this_program
::
logger_name, logger_name);
+
BaseJSONLogger new_logger = BaseJSONLogger
(
logger_name,
defaults,
this);
+
return new
_logger;
+
}
-
+
void create(string logger_name, void|mapping defaults, void|object parent_logger, void|string socket_path) {
+
::create(logger_name, defaults, parent_logger);
+
if (socket_path) { bind(socket_path); } } void destroy() { unbind_all(); } }
-
-
class MainLogger {
-
inherit Logger.SocketLogger;
-
-
mapping merge_defaults(mapping msg) {
-
mapping tmp = ([
-
"time" : time(),
-
]);
-
-
return ::merge_defaults(msg) | tmp;
-
}
-
-
void create() {
-
::create(0,0,"*:7702");
-
}
-
}
+