blob: ff9b2130abd73757e31d5b27263afb0851c86e87 [file] [log] [blame]
/**
* @brief Log storage.
*
* This file is part of HostLogger project.
*
* Copyright (c) 2018 YADRO
*
* Licensed 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 "log_storage.hpp"
#include "config.hpp"
#include "log_file.hpp"
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <exception>
void LogStorage::parse(const char* data, size_t len)
{
// Split log stream to separate messages.
// Stream may not be ended with EOL, so we handle this situation by
// last_complete_ flag.
size_t pos = 0;
while (pos < len)
{
// Search for EOL ('\n')
size_t eol = pos;
while (eol < len && data[eol] != '\n')
++eol;
const bool eolFound = eol < len;
const char* msgText = data + pos;
size_t msgLen = (eolFound ? eol : len) - pos;
// Remove '\r' from the end of message
while (msgLen && msgText[msgLen - 1] == '\r')
--msgLen;
// Append message to store
if (msgLen)
append(msgText, msgLen);
pos = eol + 1; // Skip '\n'
last_complete_ = eolFound;
}
}
void LogStorage::append(const char* msg, size_t len)
{
if (!last_complete_)
{
// The last message is incomplete, add msg as part of it
if (!messages_.empty())
{
Message& last_msg = *messages_.rbegin();
last_msg.text.append(msg, len);
}
}
else
{
Message new_msg;
time(&new_msg.timeStamp);
new_msg.text.assign(msg, len);
messages_.push_back(new_msg);
shrink();
}
}
void LogStorage::clear()
{
messages_.clear();
last_complete_ = true;
}
bool LogStorage::empty() const
{
return messages_.empty();
}
int LogStorage::save(const char* fileName) const
{
int rc = 0;
if (empty())
{
printf("No messages to write\n");
return 0;
}
try
{
LogFile log(fileName);
// Write full datetime stamp as the first record
const time_t& tmStart = messages_.begin()->timeStamp;
tm tmLocal;
localtime_r(&tmStart, &tmLocal);
char tmText[20]; // size of "%F %T" asciiz (YYYY-MM-DD HH:MM:SS)
strftime(tmText, sizeof(tmText), "%F %T", &tmLocal);
std::string titleMsg = ">>> Log collection started at ";
titleMsg += tmText;
log.write(tmStart, titleMsg);
// Write messages
for (auto it = messages_.begin(); it != messages_.end(); ++it)
log.write(it->timeStamp, it->text);
log.close();
}
catch (std::exception& e)
{
rc = EIO;
fprintf(stderr, "%s\n", e.what());
}
return rc;
}
void LogStorage::shrink()
{
if (loggerConfig.storageSizeLimit)
{
while (messages_.size() >
static_cast<size_t>(loggerConfig.storageSizeLimit))
messages_.pop_front();
}
if (loggerConfig.storageTimeLimit)
{
// Get time for N hours ago
time_t oldestTimeStamp;
time(&oldestTimeStamp);
oldestTimeStamp -= loggerConfig.storageTimeLimit * 60 * 60;
while (!messages_.empty() &&
messages_.begin()->timeStamp < oldestTimeStamp)
messages_.pop_front();
}
}