blob: ff9b2130abd73757e31d5b27263afb0851c86e87 [file] [log] [blame]
Artem Senichevefd5d742018-10-24 16:14:04 +03001/**
2 * @brief Log storage.
3 *
4 * This file is part of HostLogger project.
5 *
6 * Copyright (c) 2018 YADRO
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
Artem Senichevefd5d742018-10-24 16:14:04 +030021#include "log_storage.hpp"
22
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080023#include "config.hpp"
Artem Senichevafc73732020-05-09 19:04:51 +030024#include "log_file.hpp"
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080025
Artem Senichevefd5d742018-10-24 16:14:04 +030026#include <fcntl.h>
Artem Senichevefd5d742018-10-24 16:14:04 +030027#include <string.h>
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080028#include <sys/stat.h>
29#include <unistd.h>
Artem Senichevefd5d742018-10-24 16:14:04 +030030
Artem Senichevafc73732020-05-09 19:04:51 +030031#include <exception>
Artem Senichevefd5d742018-10-24 16:14:04 +030032
Artem Senichevefd5d742018-10-24 16:14:04 +030033void LogStorage::parse(const char* data, size_t len)
34{
35 // Split log stream to separate messages.
36 // Stream may not be ended with EOL, so we handle this situation by
37 // last_complete_ flag.
38 size_t pos = 0;
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080039 while (pos < len)
40 {
Artem Senichevefd5d742018-10-24 16:14:04 +030041 // Search for EOL ('\n')
42 size_t eol = pos;
43 while (eol < len && data[eol] != '\n')
44 ++eol;
45 const bool eolFound = eol < len;
46 const char* msgText = data + pos;
47 size_t msgLen = (eolFound ? eol : len) - pos;
48
49 // Remove '\r' from the end of message
50 while (msgLen && msgText[msgLen - 1] == '\r')
51 --msgLen;
52
53 // Append message to store
54 if (msgLen)
55 append(msgText, msgLen);
56
57 pos = eol + 1; // Skip '\n'
58 last_complete_ = eolFound;
59 }
60}
61
Artem Senichevefd5d742018-10-24 16:14:04 +030062void LogStorage::append(const char* msg, size_t len)
63{
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080064 if (!last_complete_)
65 {
Artem Senichevefd5d742018-10-24 16:14:04 +030066 // The last message is incomplete, add msg as part of it
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080067 if (!messages_.empty())
68 {
Artem Senichevefd5d742018-10-24 16:14:04 +030069 Message& last_msg = *messages_.rbegin();
70 last_msg.text.append(msg, len);
71 }
72 }
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080073 else
74 {
Artem Senichevefd5d742018-10-24 16:14:04 +030075 Message new_msg;
76 time(&new_msg.timeStamp);
77 new_msg.text.assign(msg, len);
78 messages_.push_back(new_msg);
79 shrink();
80 }
81}
82
Artem Senichevefd5d742018-10-24 16:14:04 +030083void LogStorage::clear()
84{
85 messages_.clear();
86 last_complete_ = true;
87}
88
Artem Senichevefd5d742018-10-24 16:14:04 +030089bool LogStorage::empty() const
90{
91 return messages_.empty();
92}
93
Artem Senichevafc73732020-05-09 19:04:51 +030094int LogStorage::save(const char* fileName) const
Artem Senichevefd5d742018-10-24 16:14:04 +030095{
96 int rc = 0;
97
Patrick Venture4d5a5dc2018-11-14 08:51:13 -080098 if (empty())
99 {
Artem Senichevefd5d742018-10-24 16:14:04 +0300100 printf("No messages to write\n");
101 return 0;
102 }
103
Artem Senichevafc73732020-05-09 19:04:51 +0300104 try
Patrick Venture4d5a5dc2018-11-14 08:51:13 -0800105 {
Artem Senichevafc73732020-05-09 19:04:51 +0300106 LogFile log(fileName);
107
108 // Write full datetime stamp as the first record
109 const time_t& tmStart = messages_.begin()->timeStamp;
110 tm tmLocal;
111 localtime_r(&tmStart, &tmLocal);
112 char tmText[20]; // size of "%F %T" asciiz (YYYY-MM-DD HH:MM:SS)
113 strftime(tmText, sizeof(tmText), "%F %T", &tmLocal);
114 std::string titleMsg = ">>> Log collection started at ";
115 titleMsg += tmText;
116 log.write(tmStart, titleMsg);
117
118 // Write messages
119 for (auto it = messages_.begin(); it != messages_.end(); ++it)
120 log.write(it->timeStamp, it->text);
121
122 log.close();
Artem Senichevefd5d742018-10-24 16:14:04 +0300123 }
Artem Senichevafc73732020-05-09 19:04:51 +0300124 catch (std::exception& e)
125 {
126 rc = EIO;
127 fprintf(stderr, "%s\n", e.what());
128 }
Artem Senichevefd5d742018-10-24 16:14:04 +0300129
130 return rc;
131}
132
Artem Senichevefd5d742018-10-24 16:14:04 +0300133void LogStorage::shrink()
134{
Patrick Venture4d5a5dc2018-11-14 08:51:13 -0800135 if (loggerConfig.storageSizeLimit)
136 {
137 while (messages_.size() >
138 static_cast<size_t>(loggerConfig.storageSizeLimit))
Artem Senichevefd5d742018-10-24 16:14:04 +0300139 messages_.pop_front();
140 }
Patrick Venture4d5a5dc2018-11-14 08:51:13 -0800141 if (loggerConfig.storageTimeLimit)
142 {
Artem Senichevefd5d742018-10-24 16:14:04 +0300143 // Get time for N hours ago
144 time_t oldestTimeStamp;
145 time(&oldestTimeStamp);
146 oldestTimeStamp -= loggerConfig.storageTimeLimit * 60 * 60;
Patrick Venture4d5a5dc2018-11-14 08:51:13 -0800147 while (!messages_.empty() &&
148 messages_.begin()->timeStamp < oldestTimeStamp)
Artem Senichevefd5d742018-10-24 16:14:04 +0300149 messages_.pop_front();
150 }
151}