| /** @file | |
| A brief file description | |
| @section license License | |
| 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. | |
| */ | |
| #pragma once | |
| #include "tscore/ink_platform.h" | |
| #include "proxy/logging/Log.h" | |
| #include "proxy/logging/LogFile.h" | |
| #include "proxy/logging/LogFormat.h" | |
| #include "proxy/logging/LogFilter.h" | |
| #include "proxy/logging/LogBuffer.h" | |
| #include "proxy/logging/LogAccess.h" | |
| #include "proxy/logging/LogFilter.h" | |
| #include <vector> | |
| /*------------------------------------------------------------------------- | |
| LogObject | |
| This is a new addition to the Traffic Server logging as of the 3.1 | |
| (Panda) release. This object corresponds to the new type of logfile | |
| entity that will be the focal point for all logging, replacing the role | |
| of the LogFormat object. The LogObject will contain information about | |
| the format being used, the physical file attached, and any filters that | |
| are in place. The global logging configuration for a traffic server will | |
| consist of a list of LogObjects. | |
| -------------------------------------------------------------------------*/ | |
| #define LOG_FILE_ASCII_OBJECT_FILENAME_EXTENSION ".log" | |
| #define LOG_FILE_BINARY_OBJECT_FILENAME_EXTENSION ".blog" | |
| #define LOG_FILE_PIPE_OBJECT_FILENAME_EXTENSION ".pipe" | |
| #define FLUSH_ARRAY_SIZE (512 * 4) | |
| #define LOG_OBJECT_ARRAY_DELTA 8 | |
| #define ACQUIRE_API_MUTEX(_f) \ | |
| ink_mutex_acquire(_APImutex); \ | |
| Dbg(_APImutex_dbg_ctl(), _f) | |
| #define RELEASE_API_MUTEX(_f) \ | |
| ink_mutex_release(_APImutex); \ | |
| Dbg(_APImutex_dbg_ctl(), _f) | |
| class LogBufferManager | |
| { | |
| private: | |
| ASLL(LogBuffer, write_link) write_list; | |
| int _num_flush_buffers = 0; | |
| public: | |
| LogBufferManager() {} | |
| inline void | |
| add_to_flush_queue(LogBuffer *buffer) | |
| { | |
| write_list.push(buffer); | |
| ink_atomic_increment(&_num_flush_buffers, 1); | |
| } | |
| size_t preproc_buffers(LogBufferSink *sink); | |
| }; | |
| // LogObject is atomically reference counted, and the reference count is always owned by | |
| // one or more LogObjectManagers. | |
| class LogObject : public RefCountObjInHeap | |
| { | |
| public: | |
| enum LogObjectFlags { | |
| BINARY = 1, | |
| WRITES_TO_PIPE = 4, | |
| LOG_OBJECT_FMT_TIMESTAMP = 8, // always format a timestamp into each log line (for raw text logs) | |
| }; | |
| // BINARY: log is written in binary format (rather than ascii) | |
| // WRITES_TO_PIPE: object writes to a named pipe rather than to a file | |
| LogObject(LogConfig *cfg, const LogFormat *format, const char *log_dir, const char *basename, LogFileFormat file_format, | |
| const char *header, Log::RollingEnabledValues rolling_enabled, int flush_threads, int rolling_interval_sec = 0, | |
| int rolling_offset_hr = 0, int rolling_size_mb = 0, bool auto_created = false, int rolling_max_count = 0, | |
| int rolling_min_count = 0, bool reopen_after_rolling = false, int pipe_buffer_size = 0, bool m_fast = false, | |
| unsigned binary_log_version = LOG_SEGMENT_VERSION); | |
| ~LogObject() override; | |
| void add_filter(LogFilter *filter, bool copy = true); | |
| inline void | |
| set_fmt_timestamps() | |
| { | |
| m_flags |= LOG_OBJECT_FMT_TIMESTAMP; | |
| } | |
| int log(LogAccess *lad, const char *text_entry = nullptr); | |
| /** Log the @a text_entry. | |
| * | |
| * @param lad Log accessor. | |
| * @param text_entry Literal text to log. | |
| * @return Result - value from Log::ReturnCodeFlags. | |
| * | |
| * @see Log::ReturnCodeFlags. | |
| */ | |
| int log(LogAccess *lad, std::string_view text_entry); | |
| int va_log(LogAccess *lad, const char *fmt, va_list ap); | |
| unsigned roll_files(long time_now = 0); | |
| inline size_t | |
| preproc_buffers(int idx = -1) | |
| { | |
| size_t nfb; | |
| if (idx == -1) { | |
| idx = m_buffer_manager_idx++ % m_flush_threads; | |
| } | |
| nfb = m_buffer_manager[idx].preproc_buffers(m_logFile.get()); | |
| return nfb; | |
| } | |
| void check_buffer_expiration(long time_now); | |
| void display(FILE *fd = stdout); | |
| static uint64_t compute_signature(LogFormat *format, char *filename, unsigned int flags); | |
| inline const char * | |
| get_original_filename() const | |
| { | |
| return m_filename; | |
| } | |
| inline const char * | |
| get_full_filename() const | |
| { | |
| return (m_alt_filename ? m_alt_filename : m_filename); | |
| } | |
| inline const char * | |
| get_base_filename() const | |
| { | |
| return m_basename; | |
| } | |
| off_t get_file_size_bytes(); | |
| inline uint64_t | |
| get_signature() const | |
| { | |
| return m_signature; | |
| } | |
| inline int | |
| get_rolling_interval() const | |
| { | |
| return m_rolling_interval_sec; | |
| } | |
| inline void | |
| set_log_file_header(const char *header) | |
| { | |
| m_logFile->change_header(header); | |
| } | |
| inline void | |
| set_rolling_enabled(Log::RollingEnabledValues rolling_enabled) | |
| { | |
| _setup_rolling(Log::config, rolling_enabled, m_rolling_interval_sec, m_rolling_offset_hr, m_rolling_size_mb); | |
| } | |
| inline void | |
| set_rolling_interval_sec(int rolling_interval_sec) | |
| { | |
| _setup_rolling(Log::config, m_rolling_enabled, rolling_interval_sec, m_rolling_offset_hr, m_rolling_size_mb); | |
| } | |
| inline void | |
| set_rolling_offset_hr(int rolling_offset_hr) | |
| { | |
| _setup_rolling(Log::config, m_rolling_enabled, m_rolling_interval_sec, rolling_offset_hr, m_rolling_size_mb); | |
| } | |
| inline void | |
| set_rolling_size_mb(int rolling_size_mb) | |
| { | |
| _setup_rolling(Log::config, m_rolling_enabled, m_rolling_interval_sec, m_rolling_offset_hr, rolling_size_mb); | |
| } | |
| inline bool | |
| writes_to_pipe() const | |
| { | |
| return (m_flags & WRITES_TO_PIPE) ? true : false; | |
| } | |
| inline bool | |
| writes_to_disk() | |
| { | |
| return (m_logFile && !(m_flags & WRITES_TO_PIPE) ? true : false); | |
| } | |
| inline unsigned int | |
| get_flags() const | |
| { | |
| return m_flags; | |
| } | |
| // On-disk binary segment version (logging.yaml "binary_log_version"); 2 emits | |
| // the pre-v3 layout. Binary logs only; defaults to the current version. Set at | |
| // construction (before the first buffer header is stamped), not afterward. | |
| inline unsigned | |
| get_binary_log_version() const | |
| { | |
| return m_binary_log_version; | |
| } | |
| void rename(char *new_name); | |
| inline bool | |
| has_alternate_name() const | |
| { | |
| return (m_alt_filename ? true : false); | |
| } | |
| inline const char * | |
| get_format_string() | |
| { | |
| return (m_format ? m_format->format_string() : "<none>"); | |
| } | |
| inline void | |
| force_new_buffer() | |
| { | |
| _checkout_write(nullptr, 0); | |
| } | |
| void flush_buffer(LogBuffer *); | |
| bool operator==(LogObject &rhs); | |
| public: | |
| std::unique_ptr<LogFormat> m_format; | |
| Ptr<LogFile> m_logFile; | |
| LogFilterList m_filter_list; | |
| private: | |
| char *m_basename; // the name of the file associated | |
| // with this object, relative to | |
| // the logging directory | |
| char *m_filename; // the full path of the file associated | |
| // with this object | |
| char *m_alt_filename; // the full path of the file used | |
| // instead of m_filename if the latter | |
| // could not be used because of | |
| // name conflicts | |
| unsigned int m_flags; // diverse object flags (see above) | |
| uint64_t m_signature; // INK_MD5 signature for object | |
| unsigned m_binary_log_version = LOG_SEGMENT_VERSION; // on-disk binary segment version (logging.yaml "binary_log_version") | |
| Log::RollingEnabledValues m_rolling_enabled; | |
| int m_flush_threads; // number of flush threads | |
| int m_rolling_interval_sec; // time interval between rolls | |
| // 0 means no rolling | |
| int m_rolling_offset_hr; // | |
| int m_rolling_size_mb; // size at which the log file rolls | |
| long m_last_roll_time; // the last time this object rolled its files | |
| int m_max_rolled; // maximum number of rolled logs to be kept, 0 no limit | |
| int m_min_rolled; // minimum number of rolled logs to be kept, 0 no limit | |
| bool m_reopen_after_rolling; // reopen log file after rolling (normally it is just renamed and closed) | |
| head_p m_log_buffer; // current work buffer | |
| unsigned m_buffer_manager_idx; | |
| LogBufferManager *m_buffer_manager; | |
| int m_pipe_buffer_size; | |
| bool m_fast; // use fast buffering (thread local logbuffers) | |
| void generate_filenames(const char *log_dir, const char *basename, LogFileFormat file_format); | |
| void _setup_rolling(LogConfig *cfg, Log::RollingEnabledValues rolling_enabled, int rolling_interval_sec, int rolling_offset_hr, | |
| int rolling_size_mb); | |
| unsigned _roll_files(long interval_start, long interval_end); | |
| LogBuffer *_checkout_write(size_t *write_offset, size_t write_size); | |
| // noncopyable | |
| LogObject(const LogObject &) = delete; | |
| LogObject &operator=(const LogObject &) = delete; | |
| private: | |
| // -- member functions not allowed -- | |
| LogObject(); | |
| }; | |
| /*------------------------------------------------------------------------- | |
| TextLog | |
| -------------------------------------------------------------------------*/ | |
| class TextLogObject : public LogObject | |
| { | |
| public: | |
| TextLogObject(const char *name, const char *log_dir, bool timestamps, const char *header, | |
| Log::RollingEnabledValues rolling_enabled, int flush_threads, int rolling_interval_sec, int rolling_offset_hr, | |
| int rolling_size_mb, int rolling_max_count, int rolling_min_count, bool reopen_after_rolling, bool fast = false); | |
| int write(const char *format, ...) TS_PRINTFLIKE(2, 3); | |
| int va_write(const char *format, va_list ap); | |
| static const LogFormat *textfmt; | |
| }; | |
| /*------------------------------------------------------------------------- | |
| LogObjectManager | |
| A log object manager keeps track of log objects and is responsible for | |
| their deletion | |
| -------------------------------------------------------------------------*/ | |
| class LogObjectManager | |
| { | |
| public: | |
| // error status | |
| // | |
| enum { | |
| NO_FILENAME_CONFLICTS = 0, | |
| ERROR_ACCESSING_LOG_FILE, | |
| ERROR_DETERMINING_FILE_INFO, | |
| CANNOT_SOLVE_FILENAME_CONFLICTS, | |
| ERROR_DOING_FILESYSTEM_CHECKS | |
| }; | |
| private: | |
| using LogObjectList = std::vector<LogObject *>; | |
| LogObjectList _objects; // array of configured objects | |
| LogObjectList _APIobjects; // array of API objects | |
| public: | |
| ink_mutex *_APImutex; // synchronize access to array of API objects | |
| static DbgCtl & | |
| _APImutex_dbg_ctl() | |
| { | |
| static DbgCtl dc{"log-api-mutex"}; | |
| return dc; | |
| } | |
| private: | |
| int _manage_object(LogObject *log_object, bool is_api_object, int maxConflicts); | |
| static bool _has_internal_filename_conflict(std::string_view filename, LogObjectList &objects); | |
| int _solve_filename_conflicts(LogObject *log_obj, int maxConflicts); | |
| int _solve_internal_filename_conflicts(LogObject *log_obj, int maxConflicts, int fileNum = 0); | |
| void _filename_resolution_abort(const char *fname); | |
| public: | |
| LogObjectManager(); | |
| ~LogObjectManager(); | |
| // we don't define a destructor because the objects that the | |
| // LogObjectManager manages are either passed along to another | |
| // manager or moved to the list of inactive objects to be destroyed | |
| int | |
| manage_object(LogObject *logObject, int maxConflicts = 99) | |
| { | |
| return _manage_object(logObject, false, maxConflicts); | |
| } | |
| int | |
| manage_api_object(LogObject *logObject, int maxConflicts = 99) | |
| { | |
| return _manage_object(logObject, true, maxConflicts); | |
| } | |
| // return success | |
| bool unmanage_api_object(LogObject *logObject); | |
| // Flush the buffers on all the managed log objects. | |
| void flush_all_objects(); | |
| LogObject *get_object_with_signature(uint64_t signature); | |
| void check_buffer_expiration(long time_now); | |
| unsigned roll_files(long time_now); | |
| void reopen_moved_log_files(); | |
| int log(LogAccess *lad); | |
| void display(FILE *str = stdout); | |
| void add_filter_to_all(LogFilter *filter); | |
| LogObject *find_by_format_name(const char *name) const; | |
| size_t preproc_buffers(int idx); | |
| void open_local_pipes(); | |
| void transfer_objects(LogObjectManager &mgr); | |
| bool | |
| has_api_objects() const | |
| { | |
| return _APIobjects.size() > 0; | |
| } | |
| unsigned | |
| get_num_objects() const | |
| { | |
| return _objects.size(); | |
| } | |
| }; | |
| inline bool | |
| LogObject::operator==(LogObject &old) | |
| { | |
| return (get_signature() == old.get_signature() && m_logFile && old.m_logFile && | |
| strcmp(m_logFile->get_name(), old.m_logFile->get_name()) == 0 && (m_filter_list == old.m_filter_list) && | |
| (m_rolling_interval_sec == old.m_rolling_interval_sec && m_rolling_offset_hr == old.m_rolling_offset_hr && | |
| m_rolling_size_mb == old.m_rolling_size_mb && m_reopen_after_rolling == old.m_reopen_after_rolling && | |
| m_max_rolled == old.m_max_rolled && m_min_rolled == old.m_min_rolled)); | |
| } | |
| inline off_t | |
| LogObject::get_file_size_bytes() | |
| { | |
| return m_logFile->get_size_bytes(); | |
| } |