Roxen.git / server / etc / modules / Logger.pmod

version» Context lines:

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"); -  } - } +