KiCad PCB EDA Suite
Loading...
Searching...
No Matches
app_monitor.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <app_monitor.h>
25#include <pgm_base.h>
26#include <magic_enum.hpp>
27#include <kiplatform/policy.h>
28#include <policy_keys.h>
29#include <paths.h>
30#include <wx/filename.h>
31#include <wx/ffile.h>
32
33#include <boost/uuid/uuid_io.hpp>
34#include <boost/uuid/uuid_generators.hpp>
35#include <build_version.h>
36
37#ifdef KICAD_USE_SENTRY
38#include <sentry.h>
39#endif
40
41using namespace APP_MONITOR;
42
44 m_isOptedIn( false )
45{
46}
47
48
50{
51#ifdef KICAD_USE_SENTRY
52 sentryInit();
53#endif
54}
55
56
58{
59#ifdef KICAD_USE_SENTRY
60 sentry_close();
61#endif
62}
63
64
65void SENTRY::AddTag( const wxString& aKey, const wxString& aValue )
66{
67#ifdef KICAD_USE_SENTRY
68 sentry_set_tag( aKey.c_str(), aValue.c_str() );
69#endif
70}
71
72
73void SENTRY::SetSentryOptIn( bool aOptIn )
74{
75#ifdef KICAD_USE_SENTRY
76 if( aOptIn )
77 {
78 // Opting in (when not opted in before) may require reading the uid off disk
79 // as it was skipped previously.
81
82 if( !m_sentry_optin_fn.Exists() )
83 {
84 wxFFile sentryInitFile( m_sentry_optin_fn.GetFullPath(), "w" );
85 sentryInitFile.Write( "" );
86 sentryInitFile.Close();
87 }
88
89 m_isOptedIn = true;
90 }
91 else
92 {
93 if( m_sentry_optin_fn.Exists() )
94 {
95 wxRemoveFile( m_sentry_optin_fn.GetFullPath() );
96 sentry_close();
97 }
98
99 m_isOptedIn = false;
100 }
101#else
102 m_isOptedIn = false;
103#endif
104}
105
106
108{
109 boost::uuids::uuid uuid = boost::uuids::random_generator()();
110 wxString userGuid = boost::uuids::to_string( uuid );
111
112 wxFFile sentryInitFile( m_sentry_uid_fn.GetFullPath(), "w" );
113 sentryInitFile.Write( userGuid );
114 sentryInitFile.Close();
115
116 return userGuid;
117}
118
119
121{
123}
124
125
126const wxString& SENTRY::GetSentryId()
127{
128 return m_sentryUid;
129}
130
131
133{
134 wxFFile sentryInitFile( m_sentry_uid_fn.GetFullPath() );
135 sentryInitFile.ReadAll( &m_sentryUid );
136 sentryInitFile.Close();
137
138 if( m_sentryUid.IsEmpty() || m_sentryUid.length() != 36 )
139 {
141 }
142}
143
144
146{
147#ifdef KICAD_USE_SENTRY
148 m_sentry_optin_fn = wxFileName( PATHS::GetUserCachePath(), "sentry-opt-in" );
149 m_sentry_uid_fn = wxFileName( PATHS::GetUserCachePath(), "sentry-uid" );
150
151 if( isConfiguredOptedIn() )
152 {
153 m_isOptedIn = true;
154
156
157 sentry_options_t* options = sentry_options_new();
158
159#ifndef KICAD_SENTRY_DSN
160#error "Project configuration error, missing KICAD_SENTRY_DSN"
161#endif
162 // only capture 5% of transactions
163 sentry_options_set_traces_sample_rate( options, 0.05 );
164 sentry_options_set_dsn( options, KICAD_SENTRY_DSN );
165
166 wxFileName tmp;
167 tmp.AssignDir( PATHS::GetUserCachePath() );
168 tmp.AppendDir( "sentry" );
169
170#ifdef __WINDOWS__
171 sentry_options_set_database_pathw( options, tmp.GetPathWithSep().wc_str() );
172#else
173 sentry_options_set_database_path( options, tmp.GetPathWithSep().c_str() );
174#endif
175 sentry_options_set_symbolize_stacktraces( options, true );
176 sentry_options_set_auto_session_tracking( options, false );
177
178 sentry_options_set_release( options, GetCommitHash().ToStdString().c_str() );
179
180 // This just gives us more filtering within sentry, issues still get grouped across
181 // environments.
182 sentry_options_set_environment( options, GetMajorMinorVersion().c_str() );
183
184 sentry_init( options );
185
186 sentry_value_t user = sentry_value_new_object();
187 sentry_value_set_by_key( user, "id", sentry_value_new_string( m_sentryUid.c_str() ) );
188 sentry_set_user( user );
189
190 sentry_set_tag( "kicad.version", GetBuildVersion().ToStdString().c_str() );
191 }
192#endif
193}
194
195
197{
198 // policy takes precedence
201 {
202 return policyState == KIPLATFORM::POLICY::PBOOL::ENABLED;
203 }
204
205 return m_sentry_optin_fn.Exists();
206}
207
208
210{
211#ifdef KICAD_USE_SENTRY
212 // always override with policy, just in case
215 {
216 return policyState == KIPLATFORM::POLICY::PBOOL::ENABLED;
217 }
218
219 return m_isOptedIn;
220#else
221 return false;
222#endif
223}
224
225
226void SENTRY::LogAssert( const ASSERT_CACHE_KEY& aKey, const wxString& aAssertMsg )
227{
228#ifdef KICAD_USE_SENTRY
230 {
231 return;
232 }
233
234 // We use an assert cache to avoid collecting too many events
235 // Because they can come from functions that are called hundreds of times in loops
236 if( m_assertCache.find( aKey ) == m_assertCache.end() )
237 {
238 sentry_value_t exc = sentry_value_new_exception( "assert", aAssertMsg.c_str() );
239 sentry_value_set_stacktrace( exc, NULL, 0 );
240
241 sentry_value_t sentryEvent = sentry_value_new_event();
242 sentry_event_add_exception( sentryEvent, exc );
243 sentry_capture_event( sentryEvent );
244 m_assertCache.insert( aKey );
245 }
246#endif
247}
248
249
250void SENTRY::LogException( const wxString& aMsg )
251{
252#ifdef KICAD_USE_SENTRY
254 {
255 return;
256 }
257
258 sentry_value_t exc = sentry_value_new_exception( "exception", aMsg.c_str() );
259 sentry_value_set_stacktrace( exc, NULL, 0 );
260
261 sentry_value_t sentryEvent = sentry_value_new_event();
262 sentry_event_add_exception( sentryEvent, exc );
263 sentry_capture_event( sentryEvent );
264#endif
265}
266
267#ifdef KICAD_USE_SENTRY
268static std::string GetSentryBreadCrumbType( BREADCRUMB_TYPE aType )
269{
270 // the only special case due to collisions with defines
271 if( aType == BREADCRUMB_TYPE::DBG )
272 return "debug";
273
274 if( aType == BREADCRUMB_TYPE::ERR )
275 return "error";
276
277 std::string ret( magic_enum::enum_name( aType ) );
278
279 std::transform( ret.begin(), ret.end(), ret.begin(),
280 []( unsigned char c )
281 {
282 return std::tolower( c );
283 } );
284
285 return ret;
286}
287
288
289static std::string GetSentryBreadCrumbLevel( BREADCRUMB_LEVEL aLevel )
290{
291 // the only special case due to collisions with defines
292 if( aLevel == BREADCRUMB_LEVEL::DBG )
293 return "debug";
294
295 if( aLevel == BREADCRUMB_LEVEL::ERR )
296 return "error";
297
298 std::string ret( magic_enum::enum_name( aLevel ) );
299
300 std::transform( ret.begin(), ret.end(), ret.begin(),
301 []( unsigned char c )
302 {
303 return std::tolower( c );
304 } );
305
306 return ret;
307}
308#endif
309
310namespace APP_MONITOR
311{
312SENTRY* SENTRY::m_instance = nullptr;
313
314
315bool operator<( const ASSERT_CACHE_KEY& aKey1, const ASSERT_CACHE_KEY& aKey2 )
316{
317 return aKey1.file < aKey2.file || aKey1.line < aKey2.line || aKey1.func < aKey2.func || aKey1.cond < aKey2.cond;
318}
319
320
321void AddBreadcrumb( BREADCRUMB_TYPE aType, const wxString& aMsg, const wxString& aCategory, BREADCRUMB_LEVEL aLevel )
322{
323#ifdef KICAD_USE_SENTRY
324 if( !SENTRY::Instance()->IsOptedIn() )
325 {
326 return;
327 }
328
329 std::string type = GetSentryBreadCrumbType( aType );
330 sentry_value_t crumb = sentry_value_new_breadcrumb( type.c_str(), aMsg.c_str() );
331
332 sentry_value_set_by_key( crumb, "category", sentry_value_new_string( aCategory.c_str() ) );
333
334 if( aType == BREADCRUMB_TYPE::ERR )
335 {
336 std::string level = GetSentryBreadCrumbLevel( aLevel );
337 sentry_value_set_by_key( crumb, "level", sentry_value_new_string( level.c_str() ) );
338 }
339
340 sentry_add_breadcrumb( crumb );
341#endif
342}
343
344
345void AddNavigationBreadcrumb( const wxString& aMsg, const wxString& aCategory )
346{
347#ifdef KICAD_USE_SENTRY
349#endif
350}
351
352
353void AddTransactionBreadcrumb( const wxString& aMsg, const wxString& aCategory )
354{
355#ifdef KICAD_USE_SENTRY
357#endif
358}
359
360#ifdef KICAD_USE_SENTRY
361class TRANSACTION_IMPL
362{
363public:
364 TRANSACTION_IMPL( const std::string& aName, const std::string& aOperation )
365 {
366 m_ctx = sentry_transaction_context_new( aName.c_str(), aOperation.c_str() );
367 }
368
369 ~TRANSACTION_IMPL() {
370 Finish();
371
372 // note m_ctx is handled by sentry
373 }
374
375 void Start()
376 {
377 m_tx = sentry_transaction_start( m_ctx, sentry_value_new_null() );
378 }
379
380 void Finish()
381 {
382 FinishSpan();
383
384 if( m_tx )
385 {
386 sentry_transaction_finish( m_tx );
387 m_tx = nullptr;
388 }
389 }
390
391 void StartSpan( const std::string& aOperation, const std::string& aDescription )
392 {
393 if( m_span )
394 return;
395
396 if( !m_tx )
397 return;
398
399 m_span = sentry_transaction_start_child( m_tx, aOperation.c_str(), aDescription.c_str() );
400 }
401
402 void FinishSpan()
403 {
404 if( m_span )
405 {
406 sentry_span_finish( m_span );
407 m_span = nullptr;
408 }
409 }
410
411private:
412 sentry_transaction_context_t* m_ctx = nullptr;
413 sentry_transaction_t* m_tx = nullptr;
414 sentry_span_t* m_span = nullptr;
415};
416#endif
417}
418
419
420TRANSACTION::TRANSACTION( const std::string& aName, const std::string& aOperation )
421{
422#ifdef KICAD_USE_SENTRY
423 if( SENTRY::Instance()->IsOptedIn() )
424 {
425 m_impl = new TRANSACTION_IMPL( aName, aOperation );
426 }
427#endif
428}
429
430
432{
433#ifdef KICAD_USE_SENTRY
434 delete m_impl;
435#endif
436}
437
438
440{
441#ifdef KICAD_USE_SENTRY
442 if( m_impl )
443 {
444 m_impl->Start();
445 }
446#endif
447}
448
449
450void TRANSACTION::StartSpan( const std::string& aOperation, const std::string& aDescription )
451{
452#ifdef KICAD_USE_SENTRY
453 if( m_impl )
454 {
455 m_impl->StartSpan( aOperation, aDescription );
456 }
457#endif
458}
459
460
462{
463#ifdef KICAD_USE_SENTRY
464 if( m_impl )
465 {
466 m_impl->Finish();
467 }
468#endif
469}
470
471
473{
474#ifdef KICAD_USE_SENTRY
475 if( m_impl )
476 {
477 m_impl->FinishSpan();
478 }
479#endif
480}
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
wxString GetCommitHash()
Get the commit hash as a string.
wxString GetBuildVersion()
Get the full KiCad version string.
This is a singleton class intended to manage sentry.
Definition: app_monitor.h:108
std::set< ASSERT_CACHE_KEY > m_assertCache
Definition: app_monitor.h:147
wxString sentryCreateUid()
wxFileName m_sentry_uid_fn
Definition: app_monitor.h:144
void LogAssert(const ASSERT_CACHE_KEY &aKey, const wxString &aMsg)
void SetSentryOptIn(bool aOptIn)
Definition: app_monitor.cpp:73
void LogException(const wxString &aMsg)
void AddTag(const wxString &aKey, const wxString &aValue)
Definition: app_monitor.cpp:65
static SENTRY * m_instance
Definition: app_monitor.h:140
wxFileName m_sentry_optin_fn
Definition: app_monitor.h:143
static SENTRY * Instance()
Definition: app_monitor.h:112
const wxString & GetSentryId()
void StartSpan(const std::string &aOperation, const std::string &aDescription)
TRANSACTION(const std::string &aName, const std::string &aOperation)
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
Definition: paths.cpp:426
void AddTransactionBreadcrumb(const wxString &aMsg, const wxString &aCategory)
Add a transaction breadcrumb.
void AddBreadcrumb(BREADCRUMB_TYPE aType, const wxString &aMsg, const wxString &aCategory, BREADCRUMB_LEVEL aLevel)
Add a sentry breadcrumb.
void AddNavigationBreadcrumb(const wxString &aMsg, const wxString &aCategory)
Add a navigation breadcrumb.
bool operator<(const ASSERT_CACHE_KEY &aKey1, const ASSERT_CACHE_KEY &aKey2)
PBOOL GetPolicyBool(const wxString &aKey)
Definition: unix/policy.cpp:26
see class PGM_BASE
#define POLICY_KEY_DATACOLLECTION
Definition: policy_keys.h:30
This struct represents a key being used for the std::set that deduplicates asserts during this runnin...
Definition: app_monitor.h:90