1 // Copyright (c) 2010, Amar Takhar <verm@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 #include <libaegisub/fs_fwd.h>
16 
17 #include <boost/interprocess/streams/bufferstream.hpp>
18 #include <cstdint>
19 #include <memory>
20 #include <vector>
21 
22 // These macros below aren't a perm solution, it will depend on how annoying they are through
23 // actual usage, and also depends on msvc support.
24 #define LOG_SINK(section, severity) agi::log::Message(section, severity, __FILE__, __FUNCTION__, __LINE__).stream()
25 #define LOG_E(section) LOG_SINK(section, agi::log::Exception)
26 #define LOG_A(section) LOG_SINK(section, agi::log::Assert)
27 #define LOG_W(section) LOG_SINK(section, agi::log::Warning)
28 #define LOG_I(section) LOG_SINK(section, agi::log::Info)
29 #define LOG_D(section) LOG_SINK(section, agi::log::Debug)
30 
31 #define LOG_W_IF(cond, section) if (cond) LOG_SINK(section, agi::log::Warning)
32 #define LOG_I_IF(cond, section) if (cond) LOG_SINK(section, agi::log::Info)
33 #define LOG_D_IF(cond, section) if (cond) LOG_SINK(section, agi::log::Debug)
34 
35 namespace agi {
36 namespace dispatch { class Queue; }
37 namespace log {
38 
39 class LogSink;
40 
41 /// Severity levels
42 enum Severity {
43 	Exception, ///< Used when exceptions are thrown
44 	Assert,    ///< Fatal and non-fatal assert logging
45 	Warning,   ///< Warnings
46 	Info,      ///< Information only
47 	Debug      ///< Enabled by default when compiled in debug mode.
48 };
49 
50 /// Short Severity ID
51 /// Set in common/log.cpp, keep this ordered the same as Severity.
52 extern const char *Severity_ID;
53 
54 /// Global log sink.
55 extern LogSink *log;
56 
57 /// Container to hold a single message
58 struct SinkMessage {
59 	std::string message; ///< Formatted message
60 	int64_t time;        ///< Time at execution in nanoseconds since epoch
61 	const char *section; ///< Section info eg "video/open" "video/seek" etc
62 	const char *file;    ///< Source file
63 	const char *func;    ///< Function name
64 	Severity severity;   ///< Severity
65 	int line;            ///< Source line
66 };
67 
68 class Emitter;
69 
70 /// Log sink, single destination for all messages
71 class LogSink {
72 	std::vector<SinkMessage> messages;
73 	size_t next_idx = 0;
74 	std::unique_ptr<dispatch::Queue> queue;
75 
76 	/// List of pointers to emitters
77 	std::vector<std::unique_ptr<Emitter>> emitters;
78 
79 public:
80 	LogSink();
81 	~LogSink();
82 
83 	/// Insert a message into the sink.
84 	void Log(SinkMessage const& sm);
85 
86 	/// @brief Subscribe an emitter
87 	/// @param em Emitter to add
88 	void Subscribe(std::unique_ptr<Emitter> em);
89 
90 	/// @brief Unsubscribe and delete an emitter
91 	/// @param em Emitter to delete
92 	void Unsubscribe(Emitter *em);
93 
94 	/// @brief @get the complete (current) log.
95 	/// @return Const pointer to internal sink.
96 	std::vector<SinkMessage> GetMessages() const;
97 };
98 
99 /// An emitter to produce human readable output for a log sink.
100 class Emitter {
101 public:
102 	/// Destructor
~Emitter()103 	virtual ~Emitter() { }
104 
105 	/// Accept a single log entry
106 	virtual void log(SinkMessage const& sm)=0;
107 };
108 
109 /// A simple emitter which writes the log to a file in json format
110 class JsonEmitter final : public Emitter {
111 	std::unique_ptr<std::ostream> fp;
112 
113 public:
114 	/// Constructor
115 	/// @param directory Directory to write the log file in
116 	JsonEmitter(fs::path const& directory);
117 
118 	void log(SinkMessage const&) override;
119 };
120 
121 /// Generates a message and submits it to the log sink.
122 class Message {
123 	boost::interprocess::obufferstream msg;
124 	SinkMessage sm;
125 	char buffer[2048];
126 
127 public:
128 	Message(const char *section, Severity severity, const char *file, const char *func, int line);
129 	~Message();
stream()130 	std::ostream& stream() { return msg; }
131 };
132 
133 /// Emit log entries to stdout.
134 class EmitSTDOUT: public Emitter {
135 public:
136 	void log(SinkMessage const& sm) override;
137 };
138 
139 	} // namespace log
140 } // namespace agi
141