Line data Source code
1 : #include "cetlib/PluginTypeDeducer.h"
2 : #include "cetlib/ostream_handle.h"
3 : #include "fhiclcpp/ParameterSet.h"
4 : #include "fhiclcpp/types/ConfigurationTable.h"
5 :
6 : #include "cetlib/compiler_macros.h"
7 : #include "messagefacility/MessageService/ELdestination.h"
8 : #include "messagefacility/MessageService/ELostreamOutput.h"
9 : #include "messagefacility/Utilities/ELseverityLevel.h"
10 : #include "messagefacility/Utilities/exception.h"
11 :
12 : // C/C++ includes
13 : #include <algorithm>
14 : #include <iostream>
15 : #include <memory>
16 :
17 : namespace mfplugins {
18 : using namespace mf::service;
19 : using mf::ErrorObj;
20 :
21 : /// <summary>
22 : /// Parser-Friendly Message Facility destination plugin
23 : /// </summary>
24 : class ELFriendly : public ELostreamOutput
25 : {
26 : public:
27 : /**
28 : * \brief Configuration Parameters for ELFriendly
29 : */
30 : struct Config
31 : {
32 : /// Configuration parameters for ELostreamOutput
33 : fhicl::TableFragment<ELostreamOutput::Config> elOstrConfig;
34 : /// "field_delimiter" (Default: " "): String to print between each message field
35 : fhicl::Atom<std::string> delimiter = fhicl::Atom<std::string>{
36 : fhicl::Name{"field_delimiter"}, fhicl::Comment{"String to print between each message field"}, " "};
37 : };
38 : /// Used for ParameterSet validation
39 : using Parameters = fhicl::WrappedTable<Config>;
40 :
41 : public:
42 : /// <summary>
43 : /// ELFriendly Constructor
44 : /// </summary>
45 : /// <param name="pset">ParameterSet used to configure ELFriendly</param>
46 : explicit ELFriendly(Parameters const& pset);
47 :
48 : /**
49 : * \brief Fill the "Prefix" portion of the message
50 : * \param o Output stringstream
51 : * \param msg MessageFacility object containing header information
52 : */
53 : void fillPrefix(std::ostringstream& o, const ErrorObj& msg) override;
54 : /**
55 : * \brief Fill the "User Message" portion of the message
56 : * \param o Output stringstream
57 : * \param msg MessageFacility object containing header information
58 : */
59 : void fillUsrMsg(std::ostringstream& o, const ErrorObj& msg) override;
60 : /**
61 : * \brief Fill the "Suffix" portion of the message
62 : * \param o Output stringstream
63 : * \param msg MessageFacility object containing header information
64 : */
65 : void fillSuffix(std::ostringstream& o, const ErrorObj& msg) override;
66 :
67 : private:
68 : std::string delimeter_;
69 : };
70 :
71 : // END DECLARATION
72 : //======================================================================
73 : // BEGIN IMPLEMENTATION
74 :
75 : //======================================================================
76 : // ELFriendly c'tor
77 : //======================================================================
78 :
79 0 : ELFriendly::ELFriendly(Parameters const& pset)
80 0 : : ELostreamOutput(pset().elOstrConfig(), cet::ostream_handle{std::cout}, false), delimeter_(pset().delimiter()) {}
81 :
82 : //======================================================================
83 : // Message prefix filler ( overriddes ELdestination::fillPrefix )
84 : //======================================================================
85 0 : void ELFriendly::fillPrefix(std::ostringstream& oss, const ErrorObj& msg)
86 : {
87 : // if (msg.is_verbatim()) return;
88 :
89 : // Output the prologue:
90 : //
91 :
92 0 : auto const& xid = msg.xid();
93 :
94 0 : auto id = xid.id();
95 0 : auto app = xid.application();
96 0 : auto module = xid.module();
97 0 : auto subroutine = xid.subroutine();
98 0 : std::replace(id.begin(), id.end(), ' ', '-');
99 0 : std::replace(app.begin(), app.end(), ' ', '-');
100 0 : std::replace(module.begin(), module.end(), ' ', '-');
101 0 : std::replace(subroutine.begin(), subroutine.end(), ' ', '-');
102 :
103 0 : emitToken(oss, "%MSG");
104 0 : emitToken(oss, xid.severity().getSymbol());
105 0 : emitToken(oss, delimeter_);
106 0 : emitToken(oss, id);
107 0 : emitToken(oss, msg.idOverflow());
108 0 : emitToken(oss, ":");
109 0 : emitToken(oss, delimeter_);
110 :
111 : // Output serial number of message:
112 : //
113 0 : if (format_.want(SERIAL))
114 : {
115 0 : std::ostringstream s;
116 0 : s << msg.serial();
117 0 : emitToken(oss, "[serial #" + s.str() + "]");
118 0 : emitToken(oss, delimeter_);
119 0 : }
120 :
121 : // Provide further identification:
122 : //
123 0 : bool needAspace = true;
124 0 : if (format_.want(EPILOGUE_SEPARATE))
125 : {
126 0 : if (module.length() + subroutine.length() > 0)
127 : {
128 0 : emitToken(oss, "\n");
129 0 : needAspace = false;
130 : }
131 0 : else if (format_.want(TIMESTAMP) && !format_.want(TIME_SEPARATE))
132 : {
133 0 : emitToken(oss, "\n");
134 0 : needAspace = false;
135 : }
136 : }
137 0 : if (format_.want(MODULE) && (module.length() > 0))
138 : {
139 0 : if (needAspace)
140 : {
141 0 : emitToken(oss, delimeter_);
142 0 : needAspace = false;
143 : }
144 0 : emitToken(oss, module + " ");
145 : }
146 0 : if (format_.want(SUBROUTINE) && (subroutine.length() > 0))
147 : {
148 0 : if (needAspace)
149 : {
150 0 : emitToken(oss, delimeter_);
151 0 : needAspace = false;
152 : }
153 0 : emitToken(oss, subroutine + "()");
154 0 : emitToken(oss, delimeter_);
155 : }
156 :
157 : // Provide time stamp:
158 : //
159 0 : if (format_.want(TIMESTAMP))
160 : {
161 0 : if (format_.want(TIME_SEPARATE))
162 : {
163 0 : emitToken(oss, "\n");
164 0 : needAspace = false;
165 : }
166 0 : if (needAspace)
167 : {
168 0 : emitToken(oss, delimeter_);
169 0 : needAspace = false;
170 : }
171 0 : emitToken(oss, format_.timestamp(msg.timestamp()));
172 0 : emitToken(oss, delimeter_);
173 : }
174 :
175 : // Provide the context information:
176 : //
177 0 : if (format_.want(SOME_CONTEXT))
178 : {
179 0 : if (needAspace)
180 : {
181 0 : emitToken(oss, delimeter_);
182 : // needAspace = false; // Uncomment this line if more fields are added in the future
183 : }
184 0 : emitToken(oss, msg.context());
185 : }
186 0 : }
187 :
188 : //=============================================================================
189 0 : void ELFriendly::fillUsrMsg(std::ostringstream& oss, ErrorObj const& msg)
190 : {
191 0 : if (!format_.want(TEXT))
192 : {
193 0 : return;
194 : }
195 :
196 0 : auto const usrMsgStart = std::next(msg.items().cbegin(), 4);
197 0 : auto it = msg.items().cbegin();
198 :
199 : // Determine if file and line should be included
200 :
201 : // The first four items are { " ", "<FILENAME>", ":", "<LINE>" }
202 0 : while (it != usrMsgStart)
203 : {
204 0 : if ((*it == " ") && (*std::next(it) == "--"))
205 : {
206 : // Do not emit if " --:0" is the match
207 : std::advance(it, 4);
208 : }
209 : else
210 : {
211 : // Emit if <FILENAME> and <LINE> are meaningful
212 0 : emitToken(oss, *it++);
213 : }
214 : }
215 :
216 : // Check for user-requested line breaks
217 0 : if (format_.want(NO_LINE_BREAKS))
218 : {
219 0 : emitToken(oss, " ==> ");
220 : }
221 : else
222 : {
223 0 : emitToken(oss, "", true);
224 : }
225 :
226 : // For verbatim (and user-supplied) messages, just print the contents
227 0 : auto const end = msg.items().cend();
228 0 : for (; it != end; ++it)
229 : {
230 0 : emitToken(oss, *it);
231 : }
232 : }
233 :
234 : //=============================================================================
235 0 : void ELFriendly::fillSuffix(std::ostringstream& oss, ErrorObj const& /*msg*/)
236 : {
237 0 : if (!format_.want(NO_LINE_BREAKS))
238 : {
239 0 : emitToken(oss, "\n%MSG");
240 : }
241 0 : oss << '\n';
242 0 : }
243 :
244 : } // end namespace mfplugins
245 :
246 : //======================================================================
247 : //
248 : // makePlugin function
249 : //
250 : //======================================================================
251 :
252 : #ifndef EXTERN_C_FUNC_DECLARE_START
253 : #define EXTERN_C_FUNC_DECLARE_START extern "C" {
254 : #endif
255 :
256 : EXTERN_C_FUNC_DECLARE_START
257 0 : auto makePlugin(const std::string& /*unused*/, const fhicl::ParameterSet& pset)
258 : {
259 0 : return std::make_unique<mfplugins::ELFriendly>(pset);
260 : }
261 : }
262 :
263 0 : DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
|