| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <platforms/axutil_platform_auto_sense.h> |
| #include <axutil_log_default.h> |
| #include <axutil_file_handler.h> |
| #include <axutil_thread.h> |
| #include <signal.h> |
| |
| typedef struct axutil_log_impl axutil_log_impl_t; |
| |
| static axis2_status_t |
| axutil_log_impl_rotate( |
| axutil_log_t *log); |
| |
| static void AXIS2_CALL axutil_log_impl_write( |
| axutil_log_t *log, |
| const axis2_char_t *buffer, |
| axutil_log_levels_t level, |
| const axis2_char_t *file, |
| const int line); |
| |
| AXIS2_EXTERN void AXIS2_CALL axutil_log_impl_write_to_file( |
| axutil_log_t *log, |
| axutil_thread_mutex_t *mutex, |
| axutil_log_levels_t level, |
| const axis2_char_t *file, |
| const int line, |
| const axis2_char_t *value); |
| |
| static void AXIS2_CALL axutil_log_impl_free( |
| axutil_allocator_t *allocator, |
| axutil_log_t *log); |
| |
| struct axutil_log_impl |
| { |
| axutil_log_t log; |
| void *stream; |
| axis2_char_t *file_name; |
| axutil_thread_mutex_t *mutex; |
| }; |
| |
| #define AXUTIL_INTF_TO_IMPL(log) ((axutil_log_impl_t*)(log)) |
| |
| static const axutil_log_ops_t axutil_log_ops_var = { |
| axutil_log_impl_free, |
| axutil_log_impl_write |
| }; |
| |
| static void AXIS2_CALL |
| axutil_log_impl_free( |
| axutil_allocator_t *allocator, |
| axutil_log_t *log) |
| { |
| axutil_log_impl_t *log_impl = NULL; |
| |
| if (log) |
| { |
| log_impl = AXUTIL_INTF_TO_IMPL(log); |
| |
| if (log_impl->mutex) |
| { |
| axutil_thread_mutex_destroy(log_impl->mutex); |
| } |
| if (log_impl->stream) |
| { |
| axutil_file_handler_close(log_impl->stream); |
| } |
| if (log_impl->file_name) |
| { |
| AXIS2_FREE(allocator, log_impl->file_name); |
| } |
| AXIS2_FREE(allocator, log_impl); |
| } |
| } |
| |
| AXIS2_EXTERN axutil_log_t *AXIS2_CALL |
| axutil_log_create( |
| axutil_allocator_t *allocator, |
| axutil_log_ops_t *ops, |
| const axis2_char_t *stream_name) |
| { |
| axutil_log_impl_t *log_impl; |
| axis2_char_t *path_home; |
| axis2_char_t log_file_name[AXUTIL_LOG_FILE_NAME_SIZE]; |
| axis2_char_t log_dir[AXUTIL_LOG_FILE_NAME_SIZE]; |
| axis2_char_t tmp_filename[AXUTIL_LOG_FILE_NAME_SIZE]; |
| |
| if (!allocator) |
| return NULL; |
| |
| log_impl = (axutil_log_impl_t *) AXIS2_MALLOC(allocator, |
| sizeof(axutil_log_impl_t)); |
| |
| if (!log_impl) |
| return NULL; |
| |
| log_impl->mutex = |
| axutil_thread_mutex_create(allocator, AXIS2_THREAD_MUTEX_DEFAULT); |
| |
| if (!log_impl->mutex) |
| { |
| fprintf(stderr, "cannot create log mutex \n"); |
| return NULL; |
| } |
| |
| #ifndef WIN32 |
| signal(SIGXFSZ, SIG_IGN); |
| #endif |
| |
| /* default log file is axis2.log */ |
| if (stream_name) |
| AXIS2_SNPRINTF(tmp_filename, AXUTIL_LOG_FILE_NAME_SIZE, "%s", stream_name); |
| else |
| AXIS2_SNPRINTF(tmp_filename, AXUTIL_LOG_FILE_NAME_SIZE, "%s", "axis2.log"); |
| |
| /* we write all logs to AXIS2C_HOME/logs if it is set otherwise |
| * to the working dir |
| */ |
| if (stream_name && !(axutil_rindex(stream_name, AXIS2_PATH_SEP_CHAR))) |
| { |
| path_home = AXIS2_GETENV("AXIS2C_HOME"); |
| if (path_home) |
| { |
| AXIS2_SNPRINTF(log_dir, AXUTIL_LOG_FILE_NAME_SIZE, "%s%c%s", |
| path_home, AXIS2_PATH_SEP_CHAR, "logs"); |
| if (AXIS2_SUCCESS == |
| axutil_file_handler_access(log_dir, AXIS2_F_OK)) |
| { |
| AXIS2_SNPRINTF(log_file_name, AXUTIL_LOG_FILE_NAME_SIZE, |
| "%s%c%s", log_dir, AXIS2_PATH_SEP_CHAR, tmp_filename); |
| } |
| else |
| { |
| fprintf(stderr, "log folder %s does not exist - log file %s "\ |
| "is written to . dir\n", log_dir, tmp_filename); |
| AXIS2_SNPRINTF(log_file_name, AXUTIL_LOG_FILE_NAME_SIZE, "%s", |
| tmp_filename); |
| } |
| } |
| else |
| { |
| fprintf(stderr, |
| "AXIS2C_HOME is not set - log is written to . dir\n"); |
| AXIS2_SNPRINTF(log_file_name, AXUTIL_LOG_FILE_NAME_SIZE, "%s", |
| tmp_filename); |
| } |
| } |
| else |
| { |
| AXIS2_SNPRINTF(log_file_name, AXUTIL_LOG_FILE_NAME_SIZE, "%s", |
| tmp_filename); |
| } |
| log_impl->file_name = AXIS2_MALLOC(allocator, AXUTIL_LOG_FILE_NAME_SIZE); |
| log_impl->log.size = AXUTIL_LOG_FILE_SIZE; |
| sprintf(log_impl->file_name, "%s", log_file_name); |
| |
| axutil_thread_mutex_lock(log_impl->mutex); |
| |
| log_impl->stream = axutil_file_handler_open(log_file_name, "a+"); |
| axutil_log_impl_rotate((axutil_log_t *) log_impl); |
| |
| axutil_thread_mutex_unlock(log_impl->mutex); |
| |
| if (!log_impl->stream) |
| log_impl->stream = stderr; |
| |
| /* by default, log is enabled */ |
| log_impl->log.enabled = 1; |
| log_impl->log.level = AXIS2_LOG_LEVEL_DEBUG; |
| |
| if (ops) |
| { |
| log_impl->log.ops = ops; |
| } |
| else |
| { |
| log_impl->log.ops = &axutil_log_ops_var; |
| } |
| |
| return &(log_impl->log); |
| } |
| |
| static void AXIS2_CALL |
| axutil_log_impl_write( |
| axutil_log_t *log, |
| const axis2_char_t *buffer, |
| axutil_log_levels_t level, |
| const axis2_char_t *file, |
| const int line) |
| { |
| if (log && buffer) |
| { |
| |
| if (level <= log->level) |
| { |
| const char *level_str = ""; |
| switch (level) |
| { |
| case AXIS2_LOG_LEVEL_CRITICAL: |
| level_str = "[critical] "; |
| break; |
| case AXIS2_LOG_LEVEL_ERROR: |
| level_str = "[error] "; |
| break; |
| case AXIS2_LOG_LEVEL_WARNING: |
| level_str = "[warning] "; |
| break; |
| case AXIS2_LOG_LEVEL_INFO: |
| level_str = "[info] "; |
| break; |
| case AXIS2_LOG_LEVEL_DEBUG: |
| level_str = "[debug] "; |
| break; |
| case AXIS2_LOG_LEVEL_TRACE: |
| level_str = "[...TRACE...] "; |
| break; |
| case AXIS2_LOG_LEVEL_USER: |
| break; |
| } |
| fprintf(stderr, "%s %s(%d) %s\n", level_str, file, line, buffer); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else if (buffer) |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_write_to_file( |
| axutil_log_t *log, |
| axutil_thread_mutex_t *mutex, |
| axutil_log_levels_t level, |
| const axis2_char_t *file, |
| const int line, |
| const axis2_char_t *value) |
| { |
| const char *level_str = ""; |
| axutil_log_impl_t *log_impl = AXUTIL_INTF_TO_IMPL(log); |
| FILE *fd = NULL; |
| |
| /** |
| * print all critical and error logs irrespective of log->level setting |
| */ |
| |
| switch (level) |
| { |
| case AXIS2_LOG_LEVEL_CRITICAL: |
| level_str = "[critical] "; |
| break; |
| case AXIS2_LOG_LEVEL_ERROR: |
| level_str = "[error] "; |
| break; |
| case AXIS2_LOG_LEVEL_WARNING: |
| level_str = "[warning] "; |
| break; |
| case AXIS2_LOG_LEVEL_INFO: |
| level_str = "[info] "; |
| break; |
| case AXIS2_LOG_LEVEL_DEBUG: |
| level_str = "[debug] "; |
| break; |
| case AXIS2_LOG_LEVEL_TRACE: |
| level_str = "[...TRACE...] "; |
| break; |
| case AXIS2_LOG_LEVEL_USER: |
| break; |
| } |
| axutil_thread_mutex_lock(mutex); |
| |
| axutil_log_impl_rotate(log); |
| fd = log_impl->stream; |
| |
| if (fd) |
| { |
| if (file) |
| fprintf(fd, "[%s] %s%s(%d) %s\n", axutil_log_impl_get_time_str(), |
| level_str, file, line, value); |
| else |
| fprintf(fd, "[%s] %s %s\n", axutil_log_impl_get_time_str(), level_str, |
| value); |
| fflush(fd); |
| } |
| axutil_thread_mutex_unlock(mutex); |
| } |
| |
| static axis2_status_t |
| axutil_log_impl_rotate( |
| axutil_log_t *log) |
| { |
| long size = -1; |
| FILE *old_log_fd = NULL; |
| axis2_char_t old_log_file_name[AXUTIL_LOG_FILE_NAME_SIZE]; |
| axutil_log_impl_t *log_impl = AXUTIL_INTF_TO_IMPL(log); |
| if(log_impl->file_name) |
| size = axutil_file_handler_size(log_impl->file_name); |
| |
| if(size >= log->size) |
| { |
| AXIS2_SNPRINTF(old_log_file_name, AXUTIL_LOG_FILE_NAME_SIZE, "%s%s", |
| log_impl->file_name, ".old"); |
| axutil_file_handler_close(log_impl->stream); |
| old_log_fd = axutil_file_handler_open(old_log_file_name, "w+"); |
| log_impl->stream = axutil_file_handler_open(log_impl->file_name, "r"); |
| if(old_log_fd && log_impl->stream) |
| { |
| axutil_file_handler_copy(log_impl->stream, old_log_fd); |
| axutil_file_handler_close(old_log_fd); |
| axutil_file_handler_close(log_impl->stream); |
| old_log_fd = NULL; |
| log_impl->stream = NULL; |
| } |
| if(old_log_fd) |
| { |
| axutil_file_handler_close(old_log_fd); |
| } |
| if(log_impl->stream) |
| { |
| axutil_file_handler_close(log_impl->stream); |
| } |
| log_impl->stream = axutil_file_handler_open(log_impl->file_name, "w+"); |
| } |
| return AXIS2_SUCCESS; |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_user( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| if (log && format) |
| { |
| fd = AXUTIL_INTF_TO_IMPL(log)->stream; |
| if (!fd) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| } |
| mutex = AXUTIL_INTF_TO_IMPL(log)->mutex; |
| if (!mutex) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| |
| } |
| if (AXIS2_LOG_LEVEL_DEBUG <= log->level) |
| { |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, AXIS2_LOG_LEVEL_DEBUG, |
| filename, linenumber, value); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_debug( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| if (log && format && log->enabled) |
| { |
| fd = AXUTIL_INTF_TO_IMPL(log)->stream; |
| if (!fd) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| } |
| mutex = AXUTIL_INTF_TO_IMPL(log)->mutex; |
| if (!mutex) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| |
| } |
| |
| if (AXIS2_LOG_LEVEL_DEBUG <= log->level && |
| log->level != AXIS2_LOG_LEVEL_USER) |
| { |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, AXIS2_LOG_LEVEL_DEBUG, |
| filename, linenumber, value); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_info( |
| axutil_log_t *log, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| if (log && format && log->enabled) |
| { |
| fd = AXUTIL_INTF_TO_IMPL(log)->stream; |
| if (!fd) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| |
| } |
| mutex = AXUTIL_INTF_TO_IMPL(log)->mutex; |
| if (!mutex) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| |
| } |
| |
| if (AXIS2_LOG_LEVEL_INFO <= log->level && |
| log->level != AXIS2_LOG_LEVEL_USER) |
| { |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, AXIS2_LOG_LEVEL_INFO, |
| NULL, -1, value); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_warning( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| if (log && format && log->enabled) |
| { |
| fd = AXUTIL_INTF_TO_IMPL(log)->stream; |
| if (!fd) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| |
| } |
| mutex = AXUTIL_INTF_TO_IMPL(log)->mutex; |
| if (!mutex) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| |
| } |
| |
| if (AXIS2_LOG_LEVEL_WARNING <= log->level && |
| log->level != AXIS2_LOG_LEVEL_USER) |
| { |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, |
| AXIS2_LOG_LEVEL_WARNING, filename, linenumber, value); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_error( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| if (log && format && log->enabled) |
| { |
| fd = AXUTIL_INTF_TO_IMPL(log)->stream; |
| if (!fd) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| |
| } |
| mutex = AXUTIL_INTF_TO_IMPL(log)->mutex; |
| if (!mutex) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| |
| } |
| |
| if (AXIS2_LOG_LEVEL_ERROR <= log->level) |
| { |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, AXIS2_LOG_LEVEL_ERROR, |
| filename, linenumber, value); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_critical( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| |
| if (log && format && log->enabled) |
| { |
| fd = AXUTIL_INTF_TO_IMPL(log)->stream; |
| if (!fd) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| |
| } |
| mutex = AXUTIL_INTF_TO_IMPL(log)->mutex; |
| if (!mutex) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| } |
| |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, AXIS2_LOG_LEVEL_CRITICAL, |
| filename, linenumber, value); |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| |
| } |
| |
| AXIS2_EXTERN axis2_char_t *AXIS2_CALL |
| axutil_log_impl_get_time_str( |
| void) |
| { |
| time_t tp; |
| char *time_str; |
| tp = time(&tp); |
| time_str = ctime(&tp); |
| if (!time_str) |
| { |
| return NULL; |
| } |
| if ('\n' == time_str[strlen(time_str) - 1]) |
| { |
| time_str[strlen(time_str) - 1] = '\0'; |
| } |
| return time_str; |
| } |
| |
| AXIS2_EXTERN axutil_log_t *AXIS2_CALL |
| axutil_log_create_default( |
| axutil_allocator_t *allocator) |
| { |
| axutil_log_impl_t *log_impl; |
| |
| if (!allocator) |
| return NULL; |
| |
| log_impl = (axutil_log_impl_t *) AXIS2_MALLOC(allocator, |
| sizeof(axutil_log_impl_t)); |
| |
| if (!log_impl) |
| return NULL; |
| |
| log_impl->mutex = |
| axutil_thread_mutex_create(allocator, AXIS2_THREAD_MUTEX_DEFAULT); |
| |
| if (!log_impl->mutex) |
| { |
| fprintf(stderr, "cannot create log mutex \n"); |
| return NULL; |
| } |
| |
| axutil_thread_mutex_lock(log_impl->mutex); |
| log_impl->file_name = NULL; |
| log_impl->log.size = AXUTIL_LOG_FILE_SIZE; |
| log_impl->stream = stderr; |
| axutil_thread_mutex_unlock(log_impl->mutex); |
| /* by default, log is enabled */ |
| log_impl->log.enabled = 1; |
| log_impl->log.level = AXIS2_LOG_LEVEL_DEBUG; |
| |
| log_impl->log.ops = &axutil_log_ops_var; |
| |
| return &(log_impl->log); |
| } |
| |
| #ifdef AXIS2_TRACE |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_trace( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| FILE *fd = NULL; |
| axutil_thread_mutex_t *mutex = NULL; |
| |
| if (log && format && log->enabled) |
| { |
| if (!(fd = AXUTIL_INTF_TO_IMPL(log)->stream)) |
| { |
| fprintf(stderr, "Stream is not found\n"); |
| } |
| |
| if (!(mutex = AXUTIL_INTF_TO_IMPL(log)->mutex)) |
| { |
| fprintf(stderr, "Log mutex is not found\n"); |
| } |
| |
| if (AXIS2_LOG_LEVEL_TRACE <= log->level && |
| log->level != AXIS2_LOG_LEVEL_USER) |
| { |
| char value[AXIS2_LEN_VALUE + 1]; |
| va_list ap; |
| va_start(ap, format); |
| AXIS2_VSNPRINTF(value, AXIS2_LEN_VALUE, format, ap); |
| va_end(ap); |
| axutil_log_impl_write_to_file(log, mutex, AXIS2_LOG_LEVEL_TRACE, |
| filename, linenumber, value); |
| } |
| } |
| #ifndef AXIS2_NO_LOG_FILE |
| else |
| fprintf(stderr, "please check your log and buffer"); |
| #endif |
| } |
| #else |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_impl_log_trace( |
| axutil_log_t *log, |
| const axis2_char_t *filename, |
| const int linenumber, |
| const axis2_char_t *format, |
| ...) |
| { |
| } |
| #endif |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_free( |
| axutil_allocator_t *allocator, |
| struct axutil_log *log) |
| { |
| log->ops->free(allocator, log); |
| } |
| |
| AXIS2_EXTERN void AXIS2_CALL |
| axutil_log_write( |
| axutil_log_t *log, |
| const axis2_char_t *buffer, |
| axutil_log_levels_t level, |
| const axis2_char_t *file, |
| const int line) |
| { |
| log->ops->write(log, buffer, level, file, line); |
| } |
| |