1 /*
2  *          Copyright Andrey Semashev 2007 - 2015.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 
8 #include <string>
9 #include <fstream>
10 #include <sstream>
11 #include <iostream>
12 #include <stdexcept>
13 #include <boost/smart_ptr/shared_ptr.hpp>
14 #include <boost/smart_ptr/make_shared_object.hpp>
15 #include <boost/lexical_cast.hpp>
16 #include <boost/optional/optional.hpp>
17 #include <boost/date_time/posix_time/posix_time_types.hpp>
18 #include <boost/phoenix.hpp>
19 #include <boost/log/core.hpp>
20 #include <boost/log/expressions.hpp>
21 #include <boost/log/sinks/basic_sink_backend.hpp>
22 #include <boost/log/sinks/sync_frontend.hpp>
23 #include <boost/log/sources/logger.hpp>
24 #include <boost/log/sources/record_ostream.hpp>
25 #include <boost/log/attributes/value_visitation.hpp>
26 #include <boost/log/utility/manipulators/add_value.hpp>
27 #include <boost/log/utility/setup/filter_parser.hpp>
28 #include <boost/log/utility/setup/from_stream.hpp>
29 #include <boost/log/utility/setup/from_settings.hpp>
30 
31 namespace logging = boost::log;
32 namespace src = boost::log::sources;
33 namespace expr = boost::log::expressions;
34 namespace sinks = boost::log::sinks;
35 namespace keywords = boost::log::keywords;
36 
37 //[ example_extension_stat_collector_settings_definition
38 // The backend collects statistical information about network activity of the application
39 class stat_collector :
40     public sinks::basic_sink_backend<
41         sinks::combine_requirements<
42             sinks::synchronized_feeding,
43             sinks::flushing
44         >::type
45     >
46 {
47 private:
48     // The file to write the collected information to
49     std::ofstream m_csv_file;
50 
51     // Here goes the data collected so far:
52     // Active connections
53     unsigned int m_active_connections;
54     // Sent bytes
55     unsigned int m_sent_bytes;
56     // Received bytes
57     unsigned int m_received_bytes;
58 
59     // The number of collected records since the last write to the file
60     unsigned int m_collected_count;
61     // The time when the collected data has been written to the file last time
62     boost::posix_time::ptime m_last_store_time;
63     // The collected data writing interval
64     boost::posix_time::time_duration m_write_interval;
65 
66 public:
67     // The constructor initializes the internal data
68     stat_collector(const char* file_name, boost::posix_time::time_duration write_interval);
69 
70     // The function consumes the log records that come from the frontend
71     void consume(logging::record_view const& rec);
72     // The function flushes the file
73     void flush();
74 
75 private:
76     // The function resets statistical accumulators to initial values
77     void reset_accumulators();
78     // The function writes the collected data to the file
79     void write_data();
80 };
81 //]
82 
83 // The constructor initializes the internal data
stat_collector(const char * file_name,boost::posix_time::time_duration write_interval)84 stat_collector::stat_collector(const char* file_name, boost::posix_time::time_duration write_interval) :
85     m_csv_file(file_name, std::ios::app),
86     m_active_connections(0),
87     m_last_store_time(boost::posix_time::microsec_clock::universal_time()),
88     m_write_interval(write_interval)
89 {
90     reset_accumulators();
91     if (!m_csv_file.is_open())
92         throw std::runtime_error("could not open the CSV file");
93 }
94 
95 BOOST_LOG_ATTRIBUTE_KEYWORD(sent, "Sent", unsigned int)
96 BOOST_LOG_ATTRIBUTE_KEYWORD(received, "Received", unsigned int)
97 
98 // The function consumes the log records that come from the frontend
consume(logging::record_view const & rec)99 void stat_collector::consume(logging::record_view const& rec)
100 {
101     // Accumulate statistical readings
102     if (rec.attribute_values().count("Connected"))
103         ++m_active_connections;
104     else if (rec.attribute_values().count("Disconnected"))
105         --m_active_connections;
106     else
107     {
108         namespace phoenix = boost::phoenix;
109         logging::visit(sent, rec, phoenix::ref(m_sent_bytes) += phoenix::placeholders::_1);
110         logging::visit(received, rec, phoenix::ref(m_received_bytes) += phoenix::placeholders::_1);
111     }
112     ++m_collected_count;
113 
114     // Check if it's time to write the accumulated data to the file
115     boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
116     if (now - m_last_store_time >= m_write_interval)
117     {
118         write_data();
119         m_last_store_time = now;
120     }
121 }
122 
123 // The function writes the collected data to the file
write_data()124 void stat_collector::write_data()
125 {
126     m_csv_file << m_active_connections
127         << ',' << m_sent_bytes
128         << ',' << m_received_bytes
129         << std::endl;
130     reset_accumulators();
131 }
132 
133 // The function resets statistical accumulators to initial values
reset_accumulators()134 void stat_collector::reset_accumulators()
135 {
136     m_sent_bytes = m_received_bytes = 0;
137     m_collected_count = 0;
138 }
139 
140 // The function flushes the file
flush()141 void stat_collector::flush()
142 {
143     // Store any data that may have been collected since the list write to the file
144     if (m_collected_count > 0)
145     {
146         write_data();
147         m_last_store_time = boost::posix_time::microsec_clock::universal_time();
148     }
149 
150     m_csv_file.flush();
151 }
152 
153 //[ example_extension_stat_collector_factory
154 // Factory for the stat_collector sink
155 class stat_collector_factory :
156     public logging::sink_factory< char >
157 {
158 public:
159     // Creates the sink with the provided parameters
create_sink(settings_section const & settings)160     boost::shared_ptr< sinks::sink > create_sink(settings_section const& settings)
161     {
162         // Read sink parameters
163         std::string file_name;
164         if (boost::optional< std::string > param = settings["FileName"])
165             file_name = param.get();
166         else
167             throw std::runtime_error("No target file name specified in settings");
168 
169         boost::posix_time::time_duration write_interval = boost::posix_time::minutes(1);
170         if (boost::optional< std::string > param = settings["WriteInterval"])
171         {
172             unsigned int sec = boost::lexical_cast< unsigned int >(param.get());
173             write_interval = boost::posix_time::seconds(sec);
174         }
175 
176         // Create the sink
177         boost::shared_ptr< stat_collector > backend = boost::make_shared< stat_collector >(file_name.c_str(), write_interval);
178         boost::shared_ptr< sinks::synchronous_sink< stat_collector > > sink = boost::make_shared< sinks::synchronous_sink< stat_collector > >(backend);
179 
180         if (boost::optional< std::string > param = settings["Filter"])
181         {
182             sink->set_filter(logging::parse_filter(param.get()));
183         }
184 
185         return sink;
186     }
187 };
188 
init_factories()189 void init_factories()
190 {
191     logging::register_sink_factory("StatCollector", boost::make_shared< stat_collector_factory >());
192 }
193 //]
194 
195 const char settings[] =
196     "[Sinks.MyStat]\n"
197     "Destination=StatCollector\n"
198     "FileName=stat.csv\n"
199     "WriteInterval=30\n"
200 ;
201 
init_logging()202 void init_logging()
203 {
204     init_factories();
205 
206     std::istringstream strm(settings);
207     logging::init_from_stream(strm);
208 }
209 
main(int,char * [])210 int main(int, char*[])
211 {
212     init_logging();
213 
214     src::logger lg;
215     BOOST_LOG(lg) << logging::add_value("Connected", true);
216     BOOST_LOG(lg) << logging::add_value("Sent", 100u);
217     BOOST_LOG(lg) << logging::add_value("Received", 200u);
218 
219     logging::core::get()->flush();
220 
221     return 0;
222 }
223