Skip to content

Commit

Permalink
Optional log rotation (#719)
Browse files Browse the repository at this point in the history
* Implement optional log rotation

---------

Signed-off-by: Kai-Uwe Hermann <[email protected]>
  • Loading branch information
hikinggrass authored Aug 13, 2024
1 parent 4a2f7a5 commit c2a980b
Show file tree
Hide file tree
Showing 10 changed files with 535 additions and 78 deletions.
42 changes: 34 additions & 8 deletions config/v16/profile_schemas/Internal.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@
"maxLength": 20
},
"HostName": {
"type": "string",
"readOnly": true,
"minLength": 1
"type": "string",
"readOnly": true,
"minLength": 1
},
"IFace": {
"type": "string",
"readOnly": true,
"minLength": 1
},
"readOnly": true,
"minLength": 1
},
"IMSI": {
"type": "string",
"readOnly": true,
Expand Down Expand Up @@ -158,6 +158,32 @@
"security"
]
},
"LogRotation": {
"$comment": "Enable log rotation",
"type": "boolean",
"readOnly": true,
"default": false
},
"LogRotationDateSuffix": {
"$comment": "Use a datetime suffix in log rotation files instead of the traditional .0, .1",
"type": "boolean",
"readOnly": true,
"default": false
},
"LogRotationMaximumFileSize": {
"$comment": "Maximum file size in bytes for the log file after which it will be rotated. Setting this to 0 disables log rotation.",
"type": "integer",
"readOnly": true,
"default": 0,
"minimum": 0
},
"LogRotationMaximumFileCount": {
"$comment": "Maximum amount of files before rotated logs will be deleted. Setting this to 0 disables log rotation.",
"type": "integer",
"readOnly": true,
"default": 0,
"minimum": 0
},
"SupportedChargingProfilePurposeTypes": {
"$comment": "Indicates which ChargingProfilePurposeTypes are supported. SetChargingProfile.req for profiles not listed will be rejected.",
"type": "array",
Expand Down Expand Up @@ -206,7 +232,7 @@
"type": "boolean",
"readOnly": false,
"default": false
},
},
"OcspRequestInterval": {
"$comment": "Interval in seconds used to request OCSP revocation status information on the CSO Sub-CA certificates",
"type": "integer",
Expand Down Expand Up @@ -272,4 +298,4 @@
}
},
"additionalProperties": false
}
}
64 changes: 64 additions & 0 deletions config/v201/component_schemas/standardized/InternalCtrlr.json
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,70 @@
"default": "log,html,security",
"type": "string"
},
"LogRotation": {
"variable_name": "LogRotation",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
}
],
"description": "Enable log rotation",
"default": false,
"type": "boolean"
},
"LogRotationDateSuffix": {
"variable_name": "LogRotationDateSuffix",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
}
],
"description": "Use a datetime suffix in log rotation files instead of the traditional .0, .1",
"default": false,
"type": "boolean"
},
"LogRotationMaximumFileSize": {
"variable_name": "LogRotationMaximumFileSize",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
}
],
"description": "Maximum file size in bytes for the log file after which it will be rotated. Setting this to 0 disables log rotation.",
"default": 0,
"type": "integer"
},
"LogRotationMaximumFileCount": {
"variable_name": "LogRotationMaximumFileCount",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
}
],
"description": "Maximum amount of files before rotated logs will be deleted. Setting this to 0 disables log rotation.",
"default": 0,
"type": "integer"
},
"SupportedChargingProfilePurposeTypes": {
"variable_name": "SupportedChargingProfilePurposeTypes",
"characteristics": {
Expand Down
101 changes: 90 additions & 11 deletions include/ocpp/common/ocpp_logging.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_COMMON_LOGGING_HPP
#define OCPP_COMMON_LOGGING_HPP

#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
Expand All @@ -13,56 +14,134 @@

namespace ocpp {

/// Container for a formatted OCPP message with a message type
struct FormattedMessageWithType {
std::string message_type;
std::string message;
std::string message_type; ///< The message type
std::string message; ///< The message content
};

/// Configuration for log rotation
struct LogRotationConfig {
bool date_suffix; ///< If set to true the log rotation files use a date after the ".", if not use the traditional
///< .0, .1 ... style
uint64_t
maximum_file_size_bytes; ///< The maximum size of the log file in bytes after which the file will be rotated
uint64_t maximum_file_count; ///< The maximum number of log files to keep in rotation

LogRotationConfig(bool date_suffix, uint64_t maximum_file_size_bytes, uint64_t maximum_file_count) :
date_suffix(date_suffix),
maximum_file_size_bytes(maximum_file_size_bytes),
maximum_file_count(maximum_file_count) {
}
};

///
/// \brief contains a ocpp message logging abstraction
///
class MessageLogging {
private:
bool log_messages;
std::string message_log_path;
std::string message_log_path; // FIXME: use fs::path here
std::string output_file_name;
bool log_to_console;
bool detailed_log_to_console;
bool log_to_file;
bool log_to_html;
bool log_security;
bool session_logging;
std::ofstream output_file;
std::ofstream html_log_file;
std::ofstream security_log_file;
std::filesystem::path log_file;
std::ofstream log_os;
std::filesystem::path html_log_file;
std::ofstream html_log_os;
std::filesystem::path security_log_file;
std::ofstream security_log_os;
std::mutex output_file_mutex;
std::function<void(const std::string& message, MessageDirection direction)> message_callback;
std::map<std::string, std::string> lookup_map;
std::recursive_mutex session_id_logging_mutex;
std::map<std::string, std::shared_ptr<MessageLogging>> session_id_logging;
bool rotate_logs;
bool date_suffix;
std::string logfile_basename;
uint64_t maximum_file_size_bytes;
uint64_t maximum_file_count;

/// \brief Initialize the OCPP message logging
void initialize();

/// \brief Output log message to the configured targets
void log_output(unsigned int typ, const std::string& message_type, const std::string& json_str);

/// \brief HTML encode the provided message \p msg
std::string html_encode(const std::string& msg);

/// \brief Format the given \p json_str with the given \p message_type
FormattedMessageWithType format_message(const std::string& message_type, const std::string& json_str);

/// \brief Add opening html tags to the given stream \p os
void open_html_tags(std::ofstream& os);

/// \brief Add closing html tags to the given stream \p os
void close_html_tags(std::ofstream& os);

/// \returns a datetime string in YearMonthDayHourMinuteSecond format
std::string get_datetime_string();

/// \returns file size of the given path or 0 if the file does not exist
std::uintmax_t file_size(const std::filesystem::path& path);

/// \brief Rotates the log at the given file \p file_basename and remove oldest file if there are more log files
/// than the maximum
void rotate_log(const std::string& file_basename);

/// \brief Rotates the log at the given \p path if needed based on the config, closing the stream \p os before
void rotate_log_if_needed(const std::filesystem::path& path, std::ofstream& os);

/// \brief Rotates the log at the given \p path if needed based on the config, calling \p before_close_of_os before
/// closing the stream \p os and calling \p after_open_of_os afterwards
void rotate_log_if_needed(const std::filesystem::path& path, std::ofstream& os,
std::function<void(std::ofstream& os)> before_close_of_os,
std::function<void(std::ofstream& os)> after_open_of_os);

public:
/// \brief Creates a new Websocket object with the providede \p configuration
/// \brief Creates a new MessageLogging object with the provided configuration
explicit MessageLogging(
bool log_messages, const std::string& message_log_path, const std::string& output_file_name,
bool log_to_console, bool detailed_log_to_console, bool log_to_file, bool log_to_html, bool log_security,
bool session_logging,
std::function<void(const std::string& message, MessageDirection direction)> message_callback);

/// \brief Creates a new MessageLogging object with the provided configuration and enabled log rotation
explicit MessageLogging(
bool log_messages, const std::string& message_log_path, const std::string& output_file_name,
bool log_to_console, bool detailed_log_to_console, bool log_to_file, bool log_to_html, bool log_security,
bool session_logging,
std::function<void(const std::string& message, MessageDirection direction)> message_callback,
LogRotationConfig log_rotation_config);
~MessageLogging();

/// \brief Log a message originating from the charge point
void charge_point(const std::string& message_type, const std::string& json_str);

/// \brief Log a message originating from the central system
void central_system(const std::string& message_type, const std::string& json_str);

/// \brief Log a system message
void sys(const std::string& msg);

/// \brief Log a security message
void security(const std::string& msg);

/// \brief Start session logging (without log rotation)
void start_session_logging(const std::string& session_id, const std::string& log_path);

/// \brief Stop session logging
void stop_session_logging(const std::string& session_id);

/// \returns The message log path
std::string get_message_log_path();

/// \returns If session logging is active
bool session_logging_active();
};

} // namespace ocpp
#endif // OCPP_COMMON_WEBSOCKET_HPP
#endif // OCPP_COMMON_LOGGING_HPP
8 changes: 8 additions & 0 deletions include/ocpp/v16/charge_point_configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ class ChargePointConfiguration {
KeyValue getLogMessagesKeyValue();
std::vector<std::string> getLogMessagesFormat();
KeyValue getLogMessagesFormatKeyValue();
bool getLogRotation();
KeyValue getLogRotationKeyValue();
bool getLogRotationDateSuffix();
KeyValue getLogRotationDateSuffixKeyValue();
uint64_t getLogRotationMaximumFileSize();
KeyValue getLogRotationMaximumFileSizeKeyValue();
uint64_t getLogRotationMaximumFileCount();
KeyValue getLogRotationMaximumFileCountKeyValue();
std::vector<ChargingProfilePurposeType> getSupportedChargingProfilePurposeTypes();
KeyValue getSupportedChargingProfilePurposeTypesKeyValue();
int32_t getMaxCompositeScheduleDuration();
Expand Down
4 changes: 4 additions & 0 deletions include/ocpp/v201/ctrlr_component_variables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ extern const RequiredComponentVariable& SupportedCiphers13;
extern const ComponentVariable& AuthorizeConnectorZeroOnConnectorOne;
extern const ComponentVariable& LogMessages;
extern const RequiredComponentVariable& LogMessagesFormat;
extern const ComponentVariable& LogRotation;
extern const ComponentVariable& LogRotationDateSuffix;
extern const ComponentVariable& LogRotationMaximumFileSize;
extern const ComponentVariable& LogRotationMaximumFileCount;
extern const ComponentVariable& SupportedChargingProfilePurposeTypes;
extern const ComponentVariable& SupportedCriteria;
extern const ComponentVariable& RoundClockAlignedTimestamps;
Expand Down
Loading

0 comments on commit c2a980b

Please sign in to comment.