pstore2
rotating_log.hpp
Go to the documentation of this file.
1 //===- include/pstore/os/rotating_log.hpp -----------------*- mode: C++ -*-===//
2 //* _ _ _ _ *
3 //* _ __ ___ | |_ __ _| |_(_)_ __ __ _ | | ___ __ _ *
4 //* | '__/ _ \| __/ _` | __| | '_ \ / _` | | |/ _ \ / _` | *
5 //* | | | (_) | || (_| | |_| | | | | (_| | | | (_) | (_| | *
6 //* |_| \___/ \__\__,_|\__|_|_| |_|\__, | |_|\___/ \__, | *
7 //* |___/ |___/ *
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the pstore project, under the Apache License v2.0 with LLVM Exceptions.
11 // See https://github.com/SNSystems/pstore/blob/master/LICENSE.txt for license
12 // information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //===----------------------------------------------------------------------===//
18 #ifndef PSTORE_OS_ROTATING_LOG_HPP
19 #define PSTORE_OS_ROTATING_LOG_HPP
20 
21 #include <iostream>
22 #include <limits>
23 #include <sstream>
24 #include <type_traits>
25 
26 #include "pstore/os/logging.hpp"
27 
28 namespace pstore {
29 
30  //* *
31  //* |_ _. _o _ .___|_ _._|_o._ _ | _ _ *
32  //* |_)(_|_>|(_ |(_)|_(_| |_|| |(_| |(_)(_| *
33  //* _| _| *
47  template <typename StreamTraits, typename FileSystemTraits>
48  class basic_rotating_log final : public basic_logger {
49  public:
61  basic_rotating_log (std::string base_name, std::streamoff max_bytes, unsigned num_backups,
62  StreamTraits const & traits = StreamTraits (),
63  FileSystemTraits const & fs_traits = FileSystemTraits ());
64  basic_rotating_log (basic_rotating_log const &) = delete;
66 
67  ~basic_rotating_log () noexcept override;
68 
69  basic_rotating_log & operator= (basic_rotating_log const &) = delete;
70  basic_rotating_log & operator= (basic_rotating_log &&) noexcept = delete;
71 
75  void log_impl (std::string const & message) override;
76 
78  bool is_open () const { return is_open_; }
79  StreamTraits & stream_traits () { return stream_traits_; }
80  FileSystemTraits & file_system_traits () { return file_system_traits_; }
81  typename StreamTraits::stream_type & stream () { return stream_; }
82 
83  private:
84  std::string make_file_name (unsigned index) const;
85  void do_rollover ();
86  void open ();
87  void close ();
88 
90  bool should_rollover (std::string const & record);
91 
92  static constexpr std::ios_base::openmode mode_flags_ =
93  std::ofstream::out | std::ofstream::app | std::ofstream::ate;
94 
95  std::streamoff const max_size_;
96  std::string const base_name_;
97  unsigned const num_backups_;
98 
99  typename StreamTraits::stream_type stream_;
100  bool is_open_ = false;
101  StreamTraits stream_traits_;
102  FileSystemTraits file_system_traits_;
103  };
104 
105  // (ctor)
106  // ~~~~~~
107  template <typename StreamTraits, typename FileSystemTraits>
109  std::string base_name, std::streamoff const max_size, unsigned const num_backups,
110  StreamTraits const & stream_traits, FileSystemTraits const & fs_traits)
111  : max_size_ (std::max (max_size, std::streamoff{0}))
112  , base_name_{std::move (base_name)}
113  , num_backups_ (num_backups)
114  , stream_ ()
115  , stream_traits_ (stream_traits)
116  , file_system_traits_ (fs_traits) {}
117 
118  // (dtor)
119  // ~~~~~~
120  template <typename StreamTraits, typename FileSystemTraits>
122  PSTORE_TRY { this->close (); }
123  PSTORE_CATCH (..., {})
124  }
125 
126  // log
127  // ~~~
128  template <typename StreamTraits, typename FileSystemTraits>
129  void
131  if (this->should_rollover (message)) {
132  this->do_rollover ();
133  }
134 
135  this->open ();
136  stream_ << message;
137  }
138 
139  // make file name
140  // ~~~~~~~~~~~~~~
141  template <typename StreamTraits, typename FileSystemTraits>
143  unsigned const index) const {
144  std::ostringstream str;
145  str << base_name_;
146  if (index > 0) {
147  str << '.' << index;
148  }
149  return str.str ();
150  }
151 
152  // open
153  // ~~~~
154  template <typename StreamTraits, typename FileSystemTraits>
156  if (!is_open_) {
157  stream_traits_.open (stream_, base_name_, mode_flags_);
158  is_open_ = true;
159  }
160  }
161 
162  // close
163  // ~~~~~
164  template <typename StreamTraits, typename FileSystemTraits>
166  if (is_open_) {
167  stream_.flush ();
168  stream_traits_.close (stream_);
169  is_open_ = false;
170  }
171  }
172 
173  // do rollover
174  // ~~~~~~~~~~~
175  template <typename StreamTraits, typename FileSystemTraits>
177  this->close ();
178 
179  for (auto index = num_backups_; index > 0; --index) {
180  std::string const & source = this->make_file_name (index - 1);
181  std::string const & dest = this->make_file_name (index);
182  if (file_system_traits_.exists (source)) {
183  if (file_system_traits_.exists (dest)) {
184  file_system_traits_.unlink (dest);
185  }
186  // TODO: compress the old files rather than simply rename them?
187  file_system_traits_.rename (source, dest);
188  }
189  }
190 
191  // Clear the stream contents. Not an issue if we're really using files (because we're
192  // using a different file) but, for example, writing to a single ostringstream we'll
193  // need to clear the output stream.
194  stream_traits_.clear (stream_);
195  }
196 
197  // should rollover
198  // ~~~~~~~~~~~~~~~
199  template <typename StreamTraits, typename FileSystemTraits>
201  std::string const & message) {
202 
203  bool resl = false;
204  // Both num_backups_ and max_size_ must be non-zero before roll-over
205  // will be enabled.
206  if (stream_.good () && num_backups_ > 0 && max_size_ > 0) {
207  using pos_type = typename StreamTraits::stream_type::pos_type;
208 
209  // Now we're simply trying to determine whether the file position (which will always
210  // be at the end of the file) plus the length of the message will exceed
211  // max_size_. Unfortunately, the way that the standard defines the result of tellp()
212  // (as char_traits<char>::pos_type which is, in turn, std::streampos, and ultimately
213  // std::fpos<>. This final type only supports a limited set of numeric operations.
214 
215  pos_type const pos = stream_.tellp ();
216  if (pos >= 0) {
217  // Work out whether appending message would cause the current file size to
218  // exceed max_size_.
219  std::streamoff const remaining = max_size_ - pos;
220  using ustreamoff = std::make_unsigned<std::streamoff>::type;
221  if (remaining < 0 || message.length () > static_cast<ustreamoff> (remaining)) {
222  resl = true;
223  }
224  }
225  }
226  return resl;
227  }
228 
230 
231 } // end namespace pstore
232 
233 #endif // PSTORE_OS_ROTATING_LOG_HPP
Definition: logging.hpp:173
bool is_open() const
(for testing)
Definition: rotating_log.hpp:78
Definition: chunked_sequence.hpp:607
Definition: logging.hpp:102
basic_rotating_log(std::string base_name, std::streamoff max_bytes, unsigned num_backups, StreamTraits const &traits=StreamTraits(), FileSystemTraits const &fs_traits=FileSystemTraits())
Definition: rotating_log.hpp:108
void log_impl(std::string const &message) override
Writes the supplied string to the log.
Definition: rotating_log.hpp:130
Definition: nonpod2.cpp:40
Definition: rotating_log.hpp:48