82c8ba | 2015-08-12 | Marcus Agehall | |
class BaseLogger {
object parent_logger;
mapping defaults = ([
"level" : INFO,
]);
enum {
TRACE = 10,
DEBUG = 20,
INFO = 30,
WARN = 40,
ERROR = 50,
FATAL = 60
};
int default_log_level = INFO;
int should_log() {
if (parent_logger)
return parent_logger->should_log();
return 1;
}
protected void do_log(mapping data) {
if (!data->level) {
data = data | ([]);
data->level = default_log_level;
}
string res = Standards.JSON.encode(data);
}
protected mapping merge_defaults(mapping msg) {
return defaults | msg;
}
void log(mapping|string data) {
if (!should_log()) {
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 create(void|mapping|function defaults, void|object parent_logger) {
this_program::defaults = defaults || ([]);
this_program::parent_logger = parent_logger;
}
this_program child(void|mapping|function defaults) {
this_program new_logger = object_program(this)(defaults, this);
return new_logger;
}
}
class SocketLogger {
inherit Logger.BaseLogger;
multiset(object) listeners = (<>);
array(object) ports;
mapping(string:object) ports_by_path = ([]);
class LogClient {
Stdio.File sock;
Stdio.Buffer output = Stdio.Buffer(4096);
int write(string|Stdio.Buffer data) {
if (stringp(data)) {
data = Stdio.Buffer(data);
}
output->add(data);
sock->set_write_callback(write_cb);
}
protected int write_cb(mixed self, Stdio.Buffer buff) {
int res = 0;
if (sizeof(output)) {
buff->add(output);
res = sizeof(output);
output->clear();
}
if (!sizeof(buff)) {
sock->set_write_callback(0);
}
return res;
}
protected void close_cb(mixed self) {
listeners[self] = 0;
object f = self->sock;
f->set_nonblocking(0,0,0);
f->set_id(0);
f->close();
destruct(f);
}
protected void read_cb(mixed self, Stdio.Buffer buf) {
buf->clear();
}
void create(Stdio.File f) {
sock = f;
f->set_id(this);
f->set_buffer_mode(Stdio.Buffer(1024), Stdio.Buffer(4096));
f->set_nonblocking(read_cb, 0, close_cb);
}
};
int should_log() {
return sizeof(listeners);
}
void do_log(mapping entry) {
string res = Standards.JSON.encode(entry);
Stdio.Buffer tmp = Stdio.Buffer(res);
tmp->add("\n");
sizeof(listeners) && indices(listeners)->write(tmp);
}
void accept_cb(object port) {
object l = port->accept();
l = LogClient(l);
listeners[l] = 1;
}
array get_bound_ports() {
return indices(ports_by_path);
}
object bind(string socket_path) {
if (ports_by_path[socket_path]) {
werror("Port already bound for logger!\n");
return UNDEFINED;
}
Stdio.Port port = Stdio.Port();
port->set_id(port);
if (search(socket_path, ":") == -1) {
int res = port->bind_unix(socket_path, accept_cb);
if (!res) {
werror("Failed to bind socket path %O for configuration logger.\n", socket_path);
return UNDEFINED;
} else {
ports_by_path[socket_path] = port;
return port;
}
} else {
sscanf(socket_path, "%s:%d", string ip, int tcp_port);
switch(ip||"") {
case "":
case "*":
ip = "::";
default:
}
int res = port->bind(tcp_port, accept_cb, ip, 1);
if (res != 1) {
werror("Failed to bind log port!\n%s\n", strerror(port->errno()));
} else {
ports_by_path[socket_path] = port;
return port;
}
}
}
int unbind(string|object socket) {
if (stringp(socket)) {
socket = ports_by_path[socket];
}
if (!objectp(socket)) {
werror("Invalid log socket used in unbind request.\n");
return 0;
}
foreach(ports_by_path; string path; Stdio.Port p) {
if (p == socket) {
p->close();
if (Stdio.exist(path)) {
if (!rm(path)) {
werror("Failed to remove log socket %s\n", path);
}
}
m_delete(ports_by_path, path);
destruct(p);
return 1;
}
}
return 0;
}
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);
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");
}
}
|