Current section
Files
Jump to
Current section
Files
c_src/membrane/log.c
/**
* Membrane Common C routines: Logging.
*
* All Rights Reserved, (c) 2017 Mateusz Nowak
*/
#include "log.h"
static ERL_NIF_TERM membrane_wrap_log_msg(ErlNifEnv *env, int level, ERL_NIF_TERM message_term, ERL_NIF_TERM tags_term);
static int membrane_route_log(ErlNifEnv *env, ErlNifEnv *msg_env, ERL_NIF_TERM term);
/**
* Builds log message and sends it to the log router.
*
* `env` - the environment of the calling process
* `msg_env` - the environment of the message and tag term
* `level` - integer
* `message` - term containing message
* `tags` - term containing list of terms
*
* Expects exactly one of `env` or `msg_env` variables to be a NULL.
* `message` and `tags` terms should be previously created with the second environment,
* which is not NULL.
*
* On success, returns true. Otherwise, false is returned.
*/
int membrane_log(ErlNifEnv *env, ErlNifEnv *msg_env, int level, ERL_NIF_TERM message, ERL_NIF_TERM tags) {
ErlNifEnv *used_env;
used_env = (env ? env : msg_env);
ERL_NIF_TERM wrapped_msg = membrane_wrap_log_msg(used_env, level, message, tags);
return membrane_route_log(env, msg_env, wrapped_msg);
}
/**
* Builds log message from string containing format and from va_list with parameters.
* It also attaches :nif tag and sends message to the log router.
*
* `env` - the environment of the calling process.
* Must be NULL if calling from a created thread.
* `level` - integer describing log level
* `format` and `va_args` - describe string that should be send
*
* On success, returns true. Otherwise, false is returned.
*/
int membrane_log_format(ErlNifEnv *env, int level, const char* log_tag, const char *format, ...) {
va_list args;
unsigned char* binary_data;
ErlNifEnv *used_env, *msg_env;
ERL_NIF_TERM bin_term, tags_term, nif_tag, element_tag;
// check if received process-bound or process-independent environment
if(env) {
msg_env = NULL;
used_env = env;
} else {
msg_env = enif_alloc_env();
used_env = msg_env;
}
// compute the space that is needed to store whole message
va_start(args, format);
size_t output_size = vsnprintf(NULL, 0, format, args);
va_end(args);
// allocate binary
binary_data = enif_make_new_binary(used_env, output_size, &bin_term);
// print string to the binary
va_start(args, format);
vsnprintf((char *)binary_data, output_size, format, args);
va_end(args);
// create tags_term
nif_tag = enif_make_atom(used_env, "nif");
element_tag = enif_make_atom(used_env, log_tag);
tags_term = enif_make_list2(used_env, nif_tag, element_tag);
int ret = membrane_log(env, msg_env, level, bin_term, tags_term);
if(!env) {
enif_free_env(msg_env);
}
return ret;
}
/**
* Converts integer containing log level to atom.
*/
ERL_NIF_TERM membrane_log_level_to_term(ErlNifEnv *env, int level) {
switch(level) {
case 0:
return enif_make_atom(env, "debug");
case 1:
return enif_make_atom(env, "info");
case 2:
return enif_make_atom(env, "warn");
default:
return enif_make_atom(env, "debug");
}
}
// PRIVATE API
/**
* Sends `term` as a erlang message to the log router.
* Expects exactly one of `env` or `msg_env` arguments to be NULL.
*
* On success, returns true. If log could not be sent, false is returned.
*/
static int membrane_route_log(ErlNifEnv *env, ErlNifEnv *msg_env, ERL_NIF_TERM term) {
ErlNifPid router_pid;
ERL_NIF_TERM router_module;
if(env) {
router_module = enif_make_atom(env, "Elixir.Membrane.Log.Router");
} else {
router_module = enif_make_atom(msg_env, "Elixir.Membrane.Log.Router");
}
if(enif_whereis_pid(env, router_module, &router_pid)) {
// router_pid found
if (enif_send(env, &router_pid, msg_env, term)) {
return 1;
}
}
return 0;
}
/**
* Wraps erlang terms to the format understandable by loggers, i.e.:
* `{:membrane_log, :info, ["message"], timestamp_nsec, [:nif]}`
* It also supplies message with current monotonic time.
*/
static ERL_NIF_TERM membrane_wrap_log_msg(ErlNifEnv *env, int level, ERL_NIF_TERM message_term, ERL_NIF_TERM tags_term) {
time_t raw_time = time(NULL);
struct tm * current_time = localtime(&raw_time);
char time_iso8601[255];
char format[] = "%Y-%m-%dT%H:%M:%SZ";
strftime(time_iso8601, 255, format, current_time);
ERL_NIF_TERM time_term = enif_make_string(env, time_iso8601, ERL_NIF_LATIN1);
ERL_NIF_TERM output_term = enif_make_tuple5(
env,
enif_make_atom(env, "membrane_log"),
membrane_log_level_to_term(env, level),
message_term,
time_term,
tags_term
);
return output_term;
}