• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 1054591953

30 Oct 2023 10:02AM UTC coverage: 80.194% (+0.06%) from 80.137%
1054591953

push

gitlab-ci

kacf
fix: Make sure `device_type` is submitted with inventory data.

Changelog: None
Ticket: None

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

6871 of 8568 relevant lines covered (80.19%)

9388.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.52
/mender-update/deployments/platform/boost_log/deployments.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <mender-update/deployments.hpp>
16

17
#include <algorithm>
18
#include <cctype>
19
#include <filesystem>
20
#include <string>
21

22
#include <boost/date_time/posix_time/posix_time.hpp>
23
#include <boost/log/attributes.hpp>
24
#include <boost/log/common.hpp>
25
#include <boost/log/sinks.hpp>
26
#include <boost/smart_ptr/shared_ptr.hpp>
27

28
#include <common/error.hpp>
29
#include <common/io.hpp>
30
#include <common/json.hpp>
31
#include <common/log.hpp>
32
#include <common/path.hpp>
33

34
namespace mender {
35
namespace update {
36
namespace deployments {
37

38
using namespace std;
39

40
namespace logging = boost::log;
41
namespace expr = boost::log::expressions;
42
namespace sinks = boost::log::sinks;
43

44
namespace fs = std::filesystem;
45

46
namespace error = mender::common::error;
47
namespace io = mender::common::io;
48
namespace json = mender::common::json;
49
namespace mlog = mender::common::log;
50
namespace path = mender::common::path;
51

52
static void JsonLogFormatter(logging::record_view const &rec, logging::formatting_ostream &strm) {
8,588✔
53
        strm << "{";
8,588✔
54

55
        auto val = logging::extract<boost::posix_time::ptime>("TimeStamp", rec);
8,588✔
56
        if (val) {
8,588✔
57
                strm << R"("timestamp":")"
58
                         << json::EscapeString(boost::posix_time::to_iso_extended_string(val.get())) << "Z"
25,762✔
59
                         << "\",";
8,588✔
60
        }
61

62
        auto level = logging::extract<mlog::LogLevel>("Severity", rec);
8,588✔
63
        if (level) {
8,588✔
64
                string lvl = mlog::ToStringLogLevel(level.get());
8,588✔
65
                strm << R"("level":")" << json::EscapeString(lvl) << "\",";
17,175✔
66
        }
67

68
        strm << R"("message":")" << json::EscapeString(*rec[expr::smessage]) << "\"}";
8,588✔
69
}
8,588✔
70

71
static const size_t kMaxExistingLogs = 5;
72
static const uintmax_t kLogsFreeSpaceRequired = 100 * 1024; // 100 KiB
73

74
error::Error DeploymentLog::PrepareLogDirectory() {
96✔
75
        fs::path dir_path(data_store_dir_);
192✔
76
        bool created = fs::create_directories(dir_path);
96✔
77
        if (created) {
96✔
78
                // should basically never happen, but if we happened to create the
79
                // directory, it's empty and thus well-prepared
80
                return error::NoError;
×
81
        }
82

83
        vector<string> old_logs;
96✔
84
        for (auto const &entry : fs::directory_iterator {dir_path}) {
1,018✔
85
                fs::path file_path = entry.path();
737✔
86
                if (!fs::is_regular_file(file_path)) {
730✔
87
                        continue;
723✔
88
                }
89

90
                string file_name = file_path.filename().string();
606✔
91

92
                if (file_name == LogFileName()) {
606✔
93
                        // this log file will be (re)used, leave it alone
94
                        continue;
36✔
95
                }
96

97
                if ((file_name.find("deployments.") != 0)
570✔
98
                        || (file_name.substr(file_name.size() - 4) != ".log")) {
581✔
99
                        continue;
559✔
100
                }
101

102
                // expected file name: deployments.NNNN.ID.log
103
                // "deployments.".size() == 12
104
                auto second_dot_pos = file_name.find('.', 12);
11✔
105
                auto last_dot_pos = file_name.find_last_of('.');
106
                if ((second_dot_pos == string::npos) || (last_dot_pos == string::npos)
15✔
107
                        || (second_dot_pos == last_dot_pos) || (second_dot_pos != 16)
10✔
108
                        || any_of(file_name.cbegin() + 12, file_name.cbegin() + second_dot_pos, [](char c) {
18✔
109
                                   return !isdigit(c);
7✔
110
                           })) {
111
                        mlog::Warning("Old deployment log with a malformed file name found: " + file_name);
4✔
112
                        continue;
4✔
113
                }
114

115
                old_logs.push_back(file_name);
7✔
116
        }
117
        std::sort(old_logs.begin(), old_logs.end());
96✔
118

119
        error_code ec;
96✔
120
        fs::space_info space_info = fs::space(dir_path, ec);
96✔
121
        if (ec) {
96✔
122
                return error::Error(
123
                        ec.default_error_condition(), "Failed to check free space for log files");
×
124
        }
125

126
        while ((old_logs.size() > 0)
127
                   && ((space_info.available < kLogsFreeSpaceRequired)
97✔
128
                           || (old_logs.size() > (kMaxExistingLogs - 1)))) {
4✔
129
                auto last_log_file = old_logs[old_logs.size() - 1];
1✔
130
                old_logs.pop_back();
1✔
131
                if (!fs::remove(dir_path / last_log_file, ec) && ec) {
1✔
132
                        return error::Error(
133
                                ec.default_error_condition(),
×
134
                                "Failed to remove old log file '" + last_log_file + "'");
×
135
                }
136
                if (space_info.available < kLogsFreeSpaceRequired) {
1✔
137
                        space_info = fs::space(dir_path, ec);
×
138
                        if (ec) {
×
139
                                return error::Error(
140
                                        ec.default_error_condition(), "Failed to check free space for log files");
×
141
                        }
142
                }
143
        }
144

145
        // now let's make sure old logs have an increasing index starting with 0001
146
        for (ssize_t i = old_logs.size() - 1; i >= 0; i--) {
102✔
147
                // "deployments.".size() == 12
148
                auto second_dot_pos = old_logs[i].find('.', 12);
6✔
149
                auto last_dot_pos = old_logs[i].find_last_of('.');
150

151
                // should never happen due the filter above when populating old_logs and
152
                // due to how these files are named
153
                assert(second_dot_pos != string::npos);
154
                assert(last_dot_pos != string::npos);
155
                assert(second_dot_pos != last_dot_pos);
156

157
                string deployment_id;
158
                if ((second_dot_pos == string::npos) || (last_dot_pos == string::npos)) {
6✔
159
                        deployment_id = "unknown_deployment";
×
160
                } else {
161
                        deployment_id =
162
                                old_logs[i].substr(second_dot_pos + 1, (last_dot_pos - second_dot_pos - 1));
12✔
163
                }
164
                stringstream ss;
12✔
165
                ss << "deployments.";
6✔
166
                ss << setfill('0') << setw(4) << to_string(i + 1);
6✔
167
                ss << "." + deployment_id;
6✔
168
                ss << ".log";
6✔
169

170
                string new_name = ss.str();
171
                fs::rename(dir_path / old_logs[i], dir_path / new_name, ec);
6✔
172
                if (ec) {
6✔
173
                        return error::Error(
174
                                ec.default_error_condition(),
×
175
                                "Failed to rename old log file '" + old_logs[i] + "'");
×
176
                }
177
        }
178

179
        return error::NoError;
96✔
180
}
181

182
error::Error DeploymentLog::BeginLogging() {
96✔
183
        auto err = PrepareLogDirectory();
96✔
184
        if (err != error::NoError) {
96✔
185
                return err;
×
186
        }
187

188
        auto ex_ofstr = io::OpenOfstream(LogFilePath(), true);
192✔
189
        if (!ex_ofstr) {
96✔
190
                return ex_ofstr.error();
×
191
        }
192

193
        auto log_stream = boost::make_shared<std::ofstream>(std::move(ex_ofstr.value()));
96✔
194
        sink_ = boost::make_shared<text_sink>();
192✔
195
        sink_->set_formatter(&JsonLogFormatter);
96✔
196
        sink_->locked_backend()->add_stream(log_stream);
192✔
197
        sink_->locked_backend()->auto_flush(true);
96✔
198

199
        logging::core::get()->add_sink(sink_);
288✔
200

201
        return error::NoError;
96✔
202
}
203

204
error::Error DeploymentLog::FinishLogging() {
96✔
205
        logging::core::get()->remove_sink(sink_);
288✔
206
        sink_.reset();
96✔
207
        return error::NoError;
96✔
208
}
209

210
string DeploymentLog::LogFileName() {
775✔
211
        return "deployments.0000." + id_ + ".log";
1,550✔
212
}
213

214
string DeploymentLog::LogFilePath() {
169✔
215
        return path::Join(data_store_dir_, LogFileName());
338✔
216
}
217

218
} // namespace deployments
219
} // namespace update
220
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc