Add metrics to the framework
[ric-plt/xapp-frame-cpp.git] / src / metrics / metrics.cpp
1 // vi: ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2020 Nokia
5         Copyright (c) 2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21 /*
22         Mnemonic:       metrics.cpp
23         Abstract:       Impementation of the metrics functions. (see metrics.hpp)
24
25
26                 void Set_source( std::string new_source );
27                 void Set_reporter( std::string new_reporter );
28                 void Push_data( std::string key, double value );
29
30         Date:           20 July 2020
31         Author:         E. Scott Daniels
32 */
33
34 #include <string.h>
35 #include <unistd.h>
36 #include <time.h>
37
38 #include <rmr/RIC_message_types.h>
39 #ifndef RIC_METRICS
40         #define RIC_METRICS 120
41 #endif
42
43 #include <iostream>
44
45 #include "msg_component.hpp"
46 #include "message.hpp"
47 #include "metrics.hpp"
48
49 extern char* __progname;                        // runtime lib supplied since we don't get argv[0]
50
51
52
53 namespace xapp {
54
55
56
57 // ------ private ----------------------------------------------
58
59
60 /*
61         Return the current time in milliseconds past the UNIX epoch.
62 */
63 static long long now( void ) {
64         struct timespec ts;
65         long long       now = 0;
66
67         clock_gettime( CLOCK_REALTIME, &ts );
68         now = (ts.tv_sec * 1000000) + (ts.tv_nsec/1000000);     // convert nano to milli and bang onto seconds
69
70         return now;
71 }
72
73
74 /*
75         Build the payload that we'll send.
76         Accepts a pointer to the payload message component in the RMR message that we are
77         to fill in, along with the maxmimum length of the payload.  Returns the length of
78         the payload that was acutally used.
79
80         Currently, the munchkin expects:
81                 {
82                         "reporter": "<string>",
83                         "generator" "<string>",
84                         "timestamp": double,
85                         "data": [
86                                 {
87                                         "id": "<string>",
88                                         "type": "<string>",
89                                         "value": double
90                                 },...
91                         ]
92                 }
93 */
94 int xapp::Metrics::build_payload( xapp::Msg_component payload, int max_len ) {
95         int used;                                                                                       // actual size of payload created
96         std::string generator;
97
98
99         if(  data.compare( "" ) == 0 ) {                                        // xAPP never pushed any data
100                 return 0;                                                                               // don't build, just bail
101         }
102
103         generator = source.compare( "" ) == 0 ? reporter : source;
104
105         used = snprintf( (char *) payload.get(), max_len,
106                         "{  "
107                         "\"reporter\": \"%s\", "
108                         "\"generator\": \"%s\", "
109                         "\"timestamp\": %lld, "
110                         "\"data\": [ %s ] "
111                         " }",
112                         reporter.c_str(),
113                         generator.c_str(),
114                         now(),
115                         data.c_str()
116         );
117
118         return used;
119 }
120
121
122 // --------------- builders/operators  -------------------------------------
123
124 /*
125         Create a new metrics "message" with a provided (empty) RMR message.
126 */
127 xapp::Metrics::Metrics( std::shared_ptr<Message> msg ) :
128         msg( msg ),
129         reporter( __progname ),
130         source( "" ),
131         data( "" )
132 { /* empty body */ }
133
134 /*
135         Build a metrics for a value source other than the calling xAPP
136 */
137 xapp::Metrics::Metrics( std::shared_ptr<Message> msg, std::string msource ) :
138         msg( msg ),
139         reporter( __progname ),
140         source( msource ),
141         data( "" )
142 { /* empty body */ }
143
144 /*
145         Build a metrics object that allows the xAPP to set it's reporter name
146         rather than assuming the programme name and the source.
147
148         The xAPP can pass "" as the msource if the intent is just to supply
149         an alternate programme name which is also the source.
150 */
151 xapp::Metrics::Metrics( std::shared_ptr<Message> msg, std::string reporter, std::string msource ) :
152         msg( msg ),
153         reporter( reporter ),
154         source( msource ),
155         data( "" )
156 { /* empty body */ }
157
158 // ------------------ copy and move support ---------------------------------
159
160 /*
161         Copy builder.  Given a source object instance (soi), create a copy.
162         Creating a copy should be avoided as it can be SLOW!
163 */
164 xapp::Metrics::Metrics( const Metrics& soi ) {
165         msg = soi.msg;
166         data = soi.data;
167         source = soi.source;
168         reporter = soi.reporter;
169 }
170
171 /*
172         Assignment operator. Simiolar to the copycat, but "this" object exists and
173         may have data that needs to be released prior to making a copy of the soi.
174 */
175 Metrics& xapp::Metrics::operator=( const Metrics& soi ) {
176         if( this != &soi ) {                            // cannot do self assignment
177                 // anything that must be freed from 'this' must be done here
178
179                 msg = soi.msg;
180                 data = soi.data;
181                 source = soi.source;
182                 reporter = soi.reporter;
183         }
184
185         return *this;
186 }
187
188 /*
189         Move builder.  Given a source object instance (soi), move the information from
190         the soi ensuring that the destriction of the soi doesn't trash things from
191         under us.
192 */
193 xapp::Metrics::Metrics( Metrics&& soi ) {
194         msg = soi.msg;          // capture pointers and copy data before setting soruce things to nil
195         data = soi.data;
196         source = soi.source;
197         reporter = soi.reporter;
198
199         soi.msg = NULL;         // prevent closing of RMR stuff on soi destroy
200 }
201
202 /*
203         Move Assignment operator. Move the message data to the existing object
204         ensure the object reference is cleaned up, and ensuring that the source
205         object references are removed.
206 */
207 Metrics& xapp::Metrics::operator=( Metrics&& soi ) {
208         if( this != &soi ) {                            // cannot do self assignment
209                 // anything that needs to be freed/delted from 'this', must be done here
210
211                 msg = soi.msg;                  // move pointers and values
212                 data = soi.data;
213                 source = soi.source;
214                 reporter = soi.reporter;
215
216                 soi.msg = NULL;                 // prevent bad things when source is destroyed
217         }
218
219         return *this;
220 }
221
222 /*
223         Destroyer.
224 */
225 xapp::Metrics::~Metrics() {
226         msg = NULL;
227 }
228
229
230 // ---- setters -------------------------------------------------
231
232 void xapp::Metrics::Set_source( std::string new_source ) {
233         source = new_source;
234 }
235
236 void xapp::Metrics::Set_reporter( std::string new_reporter ) {
237         reporter = new_reporter;
238 }
239
240 /*
241         Pushes the key/value pair onto the current data list.
242         This could be more efficent, but for now it is simple.
243 */
244 void xapp::Metrics::Push_data( std::string key, double value ) {
245         char    wbuf[1024];
246         char*   sep = (char *) " ";
247
248         if( data.compare( "" ) != 0 ) {                 // first on the list, no need for preceeding comma
249                 sep = (char *) ", ";
250         }
251
252         snprintf( wbuf, sizeof( wbuf ), "%s{ \"id\": \"%s\", \"value\": %.5f }", sep, key.c_str(), value );
253
254         data += std::string( wbuf );
255 }
256
257 // ------------------- getters ------------------------------------
258
259
260 // ------- message sending ---------------------------------------
261
262 /*
263         Send the message by building the payload and passing to RMR.
264         Returns the state of the send (true == good). If the xapp did not
265         push any data, no send is actually invoked, and true is reported.
266 */
267 bool xapp::Metrics::Send( ) {
268         int             used = 0;                       // actual num bytes used in the payload
269         bool    state = true;
270
271         used = build_payload( msg->Get_payload(), msg->Get_available_size() - 1 );
272         if( used > 0 ) {
273                 //fprintf( stderr, ">>>> sending payload: %s\n", (char *) msg->Get_payload().get() );
274                 state = msg->Send_msg(  RIC_METRICS, RMR_VOID_SUBID, used+1, NULL );
275         }
276
277         data = "";                              // clear data after the send
278
279         return state;
280 }
281
282
283
284 } // namespace