diff --git a/repository/Sentry-Core/Sentry.class.st b/repository/Sentry-Core/Sentry.class.st index 9af7156..2ee071e 100644 --- a/repository/Sentry-Core/Sentry.class.st +++ b/repository/Sentry-Core/Sentry.class.st @@ -1,132 +1,132 @@ -" -I am a facade for a global sentry client. -" -Class { - #name : #Sentry, - #superclass : #Object, - #classInstVars : [ - 'default', - 'autoSubmit', - 'enableContextData', - 'showNotification', - 'enabled' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #settings } -Sentry class >> autoSubmit [ - ^ autoSubmit ifNil: [ autoSubmit := false ] -] - -{ #category : #settings } -Sentry class >> autoSubmit: aBoolean [ - ^ autoSubmit := aBoolean -] - -{ #category : #accessing } -Sentry class >> client [ - ^ self default -] - -{ #category : #accessing } -Sentry class >> default [ - " - default := nil - " - ^ default ifNil: [ default := SentryClient new ] -] - -{ #category : #accessing } -Sentry class >> default: aDefaultClient [ - ^ default := aDefaultClient -] - -{ #category : #settings } -Sentry class >> dsn [ - ^ self default dsn isEmpty ifTrue: [ '' ] ifFalse: [ self default dsn asString ] -] - -{ #category : #settings } -Sentry class >> dsn: aString [ - "System Settings create an empty Object instead of an empty string for whatever reason, so I need to make an explicit check" - self default - dsn: - ((aString isMemberOf: Object) - ifTrue: [ '' ] - ifFalse: [ aString ]) -] - -{ #category : #settings } -Sentry class >> enableContextData [ - ^ enableContextData ifNil: [ enableContextData := false ] -] - -{ #category : #settings } -Sentry class >> enableContextData: aBoolean [ - ^ enableContextData := aBoolean -] - -{ #category : #settings } -Sentry class >> enabled [ - ^ enabled ifNil: [ enabled := true ] -] - -{ #category : #settings } -Sentry class >> enabled: aBoolean [ - enabled := aBoolean -] - -{ #category : #accessing } -Sentry class >> sdkName [ - ^ 'sentry-pharo' -] - -{ #category : #accessing } -Sentry class >> sdkVersion [ - ^ '0.1.0' -] - -{ #category : #settings } -Sentry class >> settingsOn: aBuilder [ - - (aBuilder group: #sentry) - parent: #privacy; - label: 'Sentry Error Tracking'; - noOrdering; - with: [ (aBuilder setting: #enabled) - target: self; - label: 'Enable Reporting'; - description: 'Enable submission to Sentry server.'. - (aBuilder setting: #autoSubmit) - target: self; - label: 'Automatic Submission'; - description: 'Reports will be sent automatically without user confirmation.'. - (aBuilder setting: #enableContextData) - target: self; - label: 'Send Context Data'; - description: - 'Enabling this will send context data (parameter values, etc.). Although helpful for debugging, such data can contain sensitive information.'. - (aBuilder setting: #showNotification) - target: self; - label: 'Show Notifications'; - description: 'An unobtrusive notification will appear when an error has been sent.'. - (aBuilder group: #sentrySettings) - label: 'Sentry Settings'; - noOrdering; - with: [ (aBuilder setting: #dsn) - target: self; - label: 'DSN'; - description: 'Default DSN settings'; - type: #String ] ] -] - -{ #category : #settings } -Sentry class >> showNotification [ - ^ showNotification ifNil: [ showNotification := true ] -] - -{ #category : #settings } -Sentry class >> showNotification: aBoolean [ - ^ showNotification := aBoolean -] +" +I am a facade for a global sentry client. +" +Class { + #name : #Sentry, + #superclass : #Object, + #classInstVars : [ + 'default', + 'autoSubmit', + 'enableContextData', + 'showNotification', + 'enabled' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #settings } +Sentry class >> autoSubmit [ + ^ autoSubmit ifNil: [ autoSubmit := false ] +] + +{ #category : #settings } +Sentry class >> autoSubmit: aBoolean [ + ^ autoSubmit := aBoolean +] + +{ #category : #accessing } +Sentry class >> client [ + ^ self default +] + +{ #category : #accessing } +Sentry class >> default [ + " + default := nil + " + ^ default ifNil: [ default := SentryClient new ] +] + +{ #category : #accessing } +Sentry class >> default: aDefaultClient [ + ^ default := aDefaultClient +] + +{ #category : #settings } +Sentry class >> dsn [ + ^ self default dsn isEmpty ifTrue: [ '' ] ifFalse: [ self default dsn asString ] +] + +{ #category : #settings } +Sentry class >> dsn: aString [ + "System Settings create an empty Object instead of an empty string for whatever reason, so I need to make an explicit check" + self default + dsn: + ((aString isMemberOf: Object) + ifTrue: [ '' ] + ifFalse: [ aString ]) +] + +{ #category : #settings } +Sentry class >> enableContextData [ + ^ enableContextData ifNil: [ enableContextData := false ] +] + +{ #category : #settings } +Sentry class >> enableContextData: aBoolean [ + ^ enableContextData := aBoolean +] + +{ #category : #settings } +Sentry class >> enabled [ + ^ enabled ifNil: [ enabled := true ] +] + +{ #category : #settings } +Sentry class >> enabled: aBoolean [ + enabled := aBoolean +] + +{ #category : #accessing } +Sentry class >> sdkName [ + ^ 'sentry-pharo' +] + +{ #category : #accessing } +Sentry class >> sdkVersion [ + ^ '0.1.0' +] + +{ #category : #settings } +Sentry class >> settingsOn: aBuilder [ + + (aBuilder group: #sentry) + parent: #privacy; + label: 'Sentry Error Tracking'; + noOrdering; + with: [ (aBuilder setting: #enabled) + target: self; + label: 'Enable Reporting'; + description: 'Enable submission to Sentry server.'. + (aBuilder setting: #autoSubmit) + target: self; + label: 'Automatic Submission'; + description: 'Reports will be sent automatically without user confirmation.'. + (aBuilder setting: #enableContextData) + target: self; + label: 'Send Context Data'; + description: + 'Enabling this will send context data (parameter values, etc.). Although helpful for debugging, such data can contain sensitive information.'. + (aBuilder setting: #showNotification) + target: self; + label: 'Show Notifications'; + description: 'An unobtrusive notification will appear when an error has been sent.'. + (aBuilder group: #sentrySettings) + label: 'Sentry Settings'; + noOrdering; + with: [ (aBuilder setting: #dsn) + target: self; + label: 'DSN'; + description: 'Default DSN settings'; + type: #String ] ] +] + +{ #category : #settings } +Sentry class >> showNotification [ + ^ showNotification ifNil: [ showNotification := true ] +] + +{ #category : #settings } +Sentry class >> showNotification: aBoolean [ + ^ showNotification := aBoolean +] diff --git a/repository/Sentry-Core/SentryBaseContext.class.st b/repository/Sentry-Core/SentryBaseContext.class.st index db80d06..fc4f739 100644 --- a/repository/Sentry-Core/SentryBaseContext.class.st +++ b/repository/Sentry-Core/SentryBaseContext.class.st @@ -1,15 +1,15 @@ -Class { - #name : #SentryBaseContext, - #superclass : #SentryInterface, - #category : 'Sentry-Core-Model' -} - -{ #category : #converting } -SentryBaseContext >> clear [ - self subclassResponsibility -] - -{ #category : #converting } -SentryBaseContext >> merge: aCollectionOrContext [ - self subclassResponsibility -] +Class { + #name : #SentryBaseContext, + #superclass : #SentryInterface, + #category : 'Sentry-Core-Model' +} + +{ #category : #converting } +SentryBaseContext >> clear [ + self subclassResponsibility +] + +{ #category : #converting } +SentryBaseContext >> merge: aCollectionOrContext [ + self subclassResponsibility +] diff --git a/repository/Sentry-Core/SentryBreadcrumb.class.st b/repository/Sentry-Core/SentryBreadcrumb.class.st index ff32126..a1f464c 100644 --- a/repository/Sentry-Core/SentryBreadcrumb.class.st +++ b/repository/Sentry-Core/SentryBreadcrumb.class.st @@ -1,101 +1,101 @@ -" -The breadcrumbs interface specifies a series of application events, or “breadcrumbs”, that occurred before the main event. - -https://docs.sentry.io/clientdev/interfaces/breadcrumbs/ -" -Class { - #name : #SentryBreadcrumb, - #superclass : #SentryInterface, - #instVars : [ - 'timestamp', - 'type', - 'message', - 'data', - 'category', - 'level' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #converting } -SentryBreadcrumb >> asDictionary [ - ^ {'timestamp' -> (self isoTimestampFor: self timestamp)} asDictionary - , (self asDictionary: #(category) optional: #(type message data level)) -] - -{ #category : #accessing } -SentryBreadcrumb >> category [ - ^ category -] - -{ #category : #accessing } -SentryBreadcrumb >> category: anObject [ - category := anObject -] - -{ #category : #accessing } -SentryBreadcrumb >> data [ - ^ data -] - -{ #category : #accessing } -SentryBreadcrumb >> data: anObject [ - data := anObject -] - -{ #category : #initialization } -SentryBreadcrumb >> initialize [ - super initialize. - timestamp := DateAndTime now -] - -{ #category : #formatting } -SentryBreadcrumb >> isoTimestampFor: aDateTime [ - | utc | - utc := aDateTime asUTC. - ^ String - streamContents: [ :aStream | - utc printYMDOn: aStream withLeadingSpace: false. - aStream nextPut: $T. - utc printHMSOn: aStream ] -] - -{ #category : #accessing } -SentryBreadcrumb >> level [ - ^ level -] - -{ #category : #accessing } -SentryBreadcrumb >> level: anObject [ - level := anObject -] - -{ #category : #accessing } -SentryBreadcrumb >> message [ - ^ message -] - -{ #category : #accessing } -SentryBreadcrumb >> message: anObject [ - message := anObject -] - -{ #category : #accessing } -SentryBreadcrumb >> timestamp [ - ^ timestamp -] - -{ #category : #accessing } -SentryBreadcrumb >> timestamp: anObject [ - timestamp := anObject -] - -{ #category : #accessing } -SentryBreadcrumb >> type [ - ^ type -] - -{ #category : #accessing } -SentryBreadcrumb >> type: anObject [ - type := anObject -] +" +The breadcrumbs interface specifies a series of application events, or “breadcrumbs”, that occurred before the main event. + +https://docs.sentry.io/clientdev/interfaces/breadcrumbs/ +" +Class { + #name : #SentryBreadcrumb, + #superclass : #SentryInterface, + #instVars : [ + 'timestamp', + 'type', + 'message', + 'data', + 'category', + 'level' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #converting } +SentryBreadcrumb >> asDictionary [ + ^ {'timestamp' -> (self isoTimestampFor: self timestamp)} asDictionary + , (self asDictionary: #(category) optional: #(type message data level)) +] + +{ #category : #accessing } +SentryBreadcrumb >> category [ + ^ category +] + +{ #category : #accessing } +SentryBreadcrumb >> category: anObject [ + category := anObject +] + +{ #category : #accessing } +SentryBreadcrumb >> data [ + ^ data +] + +{ #category : #accessing } +SentryBreadcrumb >> data: anObject [ + data := anObject +] + +{ #category : #initialization } +SentryBreadcrumb >> initialize [ + super initialize. + timestamp := DateAndTime now +] + +{ #category : #formatting } +SentryBreadcrumb >> isoTimestampFor: aDateTime [ + | utc | + utc := aDateTime asUTC. + ^ String + streamContents: [ :aStream | + utc printYMDOn: aStream withLeadingSpace: false. + aStream nextPut: $T. + utc printHMSOn: aStream ] +] + +{ #category : #accessing } +SentryBreadcrumb >> level [ + ^ level +] + +{ #category : #accessing } +SentryBreadcrumb >> level: anObject [ + level := anObject +] + +{ #category : #accessing } +SentryBreadcrumb >> message [ + ^ message +] + +{ #category : #accessing } +SentryBreadcrumb >> message: anObject [ + message := anObject +] + +{ #category : #accessing } +SentryBreadcrumb >> timestamp [ + ^ timestamp +] + +{ #category : #accessing } +SentryBreadcrumb >> timestamp: anObject [ + timestamp := anObject +] + +{ #category : #accessing } +SentryBreadcrumb >> type [ + ^ type +] + +{ #category : #accessing } +SentryBreadcrumb >> type: anObject [ + type := anObject +] diff --git a/repository/Sentry-Core/SentryClient.class.st b/repository/Sentry-Core/SentryClient.class.st index dedf84e..2a4b88c 100644 --- a/repository/Sentry-Core/SentryClient.class.st +++ b/repository/Sentry-Core/SentryClient.class.st @@ -1,263 +1,263 @@ -" -I am a client on which one dispatches exceptions and messages. - -Normally I shouldn't be used directly, but as an endpoint for a regular logging framework, such as Beacon (see SentryLogger). -" -Class { - #name : #SentryClient, - #superclass : #Object, - #instVars : [ - 'dsn', - 'context', - 'breadcrumbs', - 'appPackages', - 'isInApp', - 'beforeSend', - 'transport', - 'lastEvent' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #'instance creation' } -SentryClient class >> dsn: aDsn [ - ^ self new - dsn: aDsn; - yourself -] - -{ #category : #accessing } -SentryClient >> appPackages [ - ^ appPackages -] - -{ #category : #accessing } -SentryClient >> appPackages: aCollection [ - appPackages := aCollection -] - -{ #category : #accessing } -SentryClient >> beforeSend: aOneArgBlock [ - beforeSend := aOneArgBlock -] - -{ #category : #accessing } -SentryClient >> breadcrumbs [ - ^ breadcrumbs -] - -{ #category : #capturing } -SentryClient >> captureException: anException [ - ^ self captureException: anException in: [ :event | ] -] - -{ #category : #capturing } -SentryClient >> captureException: anException in: anEventBlock [ - | event | - event := SentryEvent new. - event message: anException description. - event - exception: - (SentryException new - values: - {SentryExceptionValue new - type: anException class name; - value: anException messageText; - module: anException class package name; - stacktrace: (self stacktraceFor: anException)}). - anEventBlock value: event. - ^ self send: event -] - -{ #category : #capturing } -SentryClient >> captureExceptionsDuring: aBlock [ - self captureExceptionsDuring: aBlock in: [ :event | ] -] - -{ #category : #capturing } -SentryClient >> captureExceptionsDuring: aBlock in: anEventBlock [ - aBlock - on: Exception - do: [ :err | self captureException: err in: anEventBlock ] -] - -{ #category : #capturing } -SentryClient >> captureMessage: aString [ - ^ self captureMessage: aString in: [ :event | ] -] - -{ #category : #capturing } -SentryClient >> captureMessage: aString in: anEventBlock [ - | event | - event := SentryEvent new. - event message: aString. - anEventBlock value: event. - ^ self send: event -] - -{ #category : #capturing } -SentryClient >> captureMessage: aString params: aCollection [ - ^ self captureMessage: aString params: aCollection in: [ :event | ] -] - -{ #category : #capturing } -SentryClient >> captureMessage: aString params: aCollection in: anEventBlock [ - | event | - event := SentryEvent new. - event message: (SentryMessage message: aString params: aCollection). - anEventBlock value: event. - ^ self send: event -] - -{ #category : #accessing } -SentryClient >> clearLastEvent [ - lastEvent := nil -] - -{ #category : #accessing } -SentryClient >> context [ - ^ context -] - -{ #category : #accessing } -SentryClient >> defaultTransport [ - ^ SentryHttpTransport new -] - -{ #category : #accessing } -SentryClient >> dsn [ - ^ dsn -] - -{ #category : #accessing } -SentryClient >> dsn: aDsn [ - | dsnUrl | - dsnUrl := aDsn asZnUrl. - self validateDsn: dsnUrl. - dsn := dsnUrl -] - -{ #category : #accessing } -SentryClient >> dsnString [ - ^ dsn asString -] - -{ #category : #initialization } -SentryClient >> initialize [ - super initialize. - dsn := ''. - context := SentryContext new. - appPackages := #(). - self resetBreadcrumbs. - isInApp := [ :aMethod | - self appPackages isString - ifTrue: [ self appPackages asRegex search: aMethod methodClass package name ] - ifFalse: [ self appPackages includes: aMethod methodClass package ] ]. - beforeSend := [ :anEvent | ] -] - -{ #category : #testing } -SentryClient >> isEnabled [ - ^ dsn isEmpty not & Sentry enabled -] - -{ #category : #testing } -SentryClient >> isMethodInApp: aMethod [ - ^ isInApp value: aMethod -] - -{ #category : #accessing } -SentryClient >> lastEvent [ - ^ lastEvent -] - -{ #category : #capturing } -SentryClient >> recordBreadcrumbIn: aOneArgBlock [ - | crumb | - crumb := SentryBreadcrumb new. - aOneArgBlock value: crumb. - breadcrumbs add: crumb -] - -{ #category : #initialization } -SentryClient >> resetBreadcrumbs [ - breadcrumbs := OrderedCollection new -] - -{ #category : #sending } -SentryClient >> send: anEvent [ - | payload | - self isEnabled - ifFalse: [ ^ nil ]. - self assert: [ dsn isNotNil ] description: 'DSN wasn''t configured.'. - beforeSend value: anEvent. - payload := anEvent asDictionary. - breadcrumbs - ifNotEmpty: [ payload at: 'breadcrumbs' put: (breadcrumbs collect: #asDictionary) asArray. - self resetBreadcrumbs ]. - context asDictionary ifNotEmpty: [ :ctx | payload := payload , ctx ]. - self transport send: payload to: dsn. - lastEvent := anEvent. - ^ anEvent eventId -] - -{ #category : #examples } -SentryClient >> sendSampleException [ - ^ [ self error: 'Sample Exception' ] - on: Exception - do: [ :ex | self captureException: ex in: [ :event | event level: 'sample' ] ] -] - -{ #category : #examples } -SentryClient >> sendSampleMessage [ - self captureMessage: 'Sample Message' in: [ :event | event level: 'sample' ] -] - -{ #category : #extraction } -SentryClient >> stacktraceFor: anException [ - | frames | - frames := anException signalerContext stack - collect: [ :ctx | - | method interval | - method := ctx method. - interval := ctx debuggerMap rangeForPC: ctx pc. - SentryFrame new - method: method; - code: ctx sourceCode start: interval first; - inApp: (self isMethodInApp: method); - vars: - {'_receiver' -> (ctx receiver printStringLimitedTo: 120). - '_context' -> (ctx printStringLimitedTo: 120)} asDictionary ]. - ^ SentryStacktrace new frames: frames reversed asArray -] - -{ #category : #accessing } -SentryClient >> transport [ - ^ transport ifNil: [ transport := self defaultTransport ] -] - -{ #category : #accessing } -SentryClient >> transport: aTransport [ - transport := aTransport -] - -{ #category : #validation } -SentryClient >> validateDsn: aDsn [ - "scheme://username@host/path" - - "Empty DSN disables the client." - - aDsn isEmpty ifTrue: [ ^ self ]. - self - assert: [ aDsn scheme isNotNil and: [ aDsn scheme isNotEmpty ] ] - description: 'scheme is missing in the DSN'. - self - assert: [ aDsn username isNotNil and: [ aDsn username isNotEmpty ] ] - description: 'username is missing in the DSN'. - self - assert: [ aDsn host isNotNil and: [ aDsn host isNotEmpty ] ] - description: 'host is missing in the DSN'. - self - assert: [ aDsn path isNotNil and: [ aDsn path isNotEmpty ] ] - description: 'path is missing in the DSN' -] +" +I am a client on which one dispatches exceptions and messages. + +Normally I shouldn't be used directly, but as an endpoint for a regular logging framework, such as Beacon (see SentryLogger). +" +Class { + #name : #SentryClient, + #superclass : #Object, + #instVars : [ + 'dsn', + 'context', + 'breadcrumbs', + 'appPackages', + 'isInApp', + 'beforeSend', + 'transport', + 'lastEvent' + ], + #category : #'Sentry-Core-Model' +} + +{ #category : #'instance creation' } +SentryClient class >> dsn: aDsn [ + ^ self new + dsn: aDsn; + yourself +] + +{ #category : #accessing } +SentryClient >> appPackages [ + ^ appPackages +] + +{ #category : #accessing } +SentryClient >> appPackages: aCollection [ + appPackages := aCollection +] + +{ #category : #accessing } +SentryClient >> beforeSend: aOneArgBlock [ + beforeSend := aOneArgBlock +] + +{ #category : #accessing } +SentryClient >> breadcrumbs [ + ^ breadcrumbs +] + +{ #category : #capturing } +SentryClient >> captureException: anException [ + ^ self captureException: anException in: [ :event | ] +] + +{ #category : #capturing } +SentryClient >> captureException: anException in: anEventBlock [ + | event | + event := SentryEvent new. + event message: anException description. + event + exception: + (SentryException new + values: + {SentryExceptionValue new + type: anException class name; + value: anException messageText; + module: anException class package name; + stacktrace: (self stacktraceFor: anException)}). + anEventBlock value: event. + ^ self send: event +] + +{ #category : #capturing } +SentryClient >> captureExceptionsDuring: aBlock [ + self captureExceptionsDuring: aBlock in: [ :event | ] +] + +{ #category : #capturing } +SentryClient >> captureExceptionsDuring: aBlock in: anEventBlock [ + aBlock + on: Exception + do: [ :err | self captureException: err in: anEventBlock ] +] + +{ #category : #capturing } +SentryClient >> captureMessage: aString [ + ^ self captureMessage: aString in: [ :event | ] +] + +{ #category : #capturing } +SentryClient >> captureMessage: aString in: anEventBlock [ + | event | + event := SentryEvent new. + event message: aString. + anEventBlock value: event. + ^ self send: event +] + +{ #category : #capturing } +SentryClient >> captureMessage: aString params: aCollection [ + ^ self captureMessage: aString params: aCollection in: [ :event | ] +] + +{ #category : #capturing } +SentryClient >> captureMessage: aString params: aCollection in: anEventBlock [ + | event | + event := SentryEvent new. + event message: (SentryMessage message: aString params: aCollection). + anEventBlock value: event. + ^ self send: event +] + +{ #category : #accessing } +SentryClient >> clearLastEvent [ + lastEvent := nil +] + +{ #category : #accessing } +SentryClient >> context [ + ^ context +] + +{ #category : #accessing } +SentryClient >> defaultTransport [ + ^ SentryHttpTransport new +] + +{ #category : #accessing } +SentryClient >> dsn [ + ^ dsn +] + +{ #category : #accessing } +SentryClient >> dsn: aDsn [ + | dsnUrl | + dsnUrl := aDsn asZnUrl. + self validateDsn: dsnUrl. + dsn := dsnUrl +] + +{ #category : #accessing } +SentryClient >> dsnString [ + ^ dsn asString +] + +{ #category : #initialization } +SentryClient >> initialize [ + super initialize. + dsn := ''. + context := SentryContext new. + appPackages := #(). + self resetBreadcrumbs. + isInApp := [ :aMethod | + self appPackages isString + ifTrue: [ self appPackages asRegex search: aMethod methodClass package name ] + ifFalse: [ self appPackages includes: aMethod methodClass package ] ]. + beforeSend := [ :anEvent | ] +] + +{ #category : #testing } +SentryClient >> isEnabled [ + ^ dsn isEmpty not & Sentry enabled +] + +{ #category : #testing } +SentryClient >> isMethodInApp: aMethod [ + ^ isInApp value: aMethod +] + +{ #category : #accessing } +SentryClient >> lastEvent [ + ^ lastEvent +] + +{ #category : #capturing } +SentryClient >> recordBreadcrumbIn: aOneArgBlock [ + | crumb | + crumb := SentryBreadcrumb new. + aOneArgBlock value: crumb. + breadcrumbs add: crumb +] + +{ #category : #initialization } +SentryClient >> resetBreadcrumbs [ + breadcrumbs := OrderedCollection new +] + +{ #category : #sending } +SentryClient >> send: anEvent [ + | payload | + self isEnabled + ifFalse: [ ^ nil ]. + self assert: [ dsn isNotNil ] description: 'DSN wasn''t configured.'. + beforeSend value: anEvent. + payload := anEvent asDictionary. + breadcrumbs + ifNotEmpty: [ payload at: 'breadcrumbs' put: (breadcrumbs collect: #asDictionary) asArray. + self resetBreadcrumbs ]. + context asDictionary ifNotEmpty: [ :ctx | payload := payload , ctx ]. + self transport send: payload to: dsn. + lastEvent := anEvent. + ^ anEvent eventId +] + +{ #category : #examples } +SentryClient >> sendSampleException [ + ^ [ self error: 'Sample Exception' ] + on: Exception + do: [ :ex | self captureException: ex in: [ :event | event level: 'sample' ] ] +] + +{ #category : #examples } +SentryClient >> sendSampleMessage [ + self captureMessage: 'Sample Message' in: [ :event | event level: 'sample' ] +] + +{ #category : #extraction } +SentryClient >> stacktraceFor: anException [ + | frames | + frames := anException signalerContext stack + collect: [ :ctx | + | method interval | + method := ctx method. + interval := (method sourceNodeForPC: ctx pc) debugHighlightRange. + SentryFrame new + method: method; + code: ctx sourceCode start: interval first; + inApp: (self isMethodInApp: method); + vars: + {'_receiver' -> (ctx receiver printStringLimitedTo: 120). + '_context' -> (ctx printStringLimitedTo: 120)} asDictionary ]. + ^ SentryStacktrace new frames: frames reversed asArray +] + +{ #category : #accessing } +SentryClient >> transport [ + ^ transport ifNil: [ transport := self defaultTransport ] +] + +{ #category : #accessing } +SentryClient >> transport: aTransport [ + transport := aTransport +] + +{ #category : #validation } +SentryClient >> validateDsn: aDsn [ + "scheme://username@host/path" + + "Empty DSN disables the client." + + aDsn isEmpty ifTrue: [ ^ self ]. + self + assert: [ aDsn scheme isNotNil and: [ aDsn scheme isNotEmpty ] ] + description: 'scheme is missing in the DSN'. + self + assert: [ aDsn username isNotNil and: [ aDsn username isNotEmpty ] ] + description: 'username is missing in the DSN'. + self + assert: [ aDsn host isNotNil and: [ aDsn host isNotEmpty ] ] + description: 'host is missing in the DSN'. + self + assert: [ aDsn path isNotNil and: [ aDsn path isNotEmpty ] ] + description: 'path is missing in the DSN' +] diff --git a/repository/Sentry-Core/SentryContext.class.st b/repository/Sentry-Core/SentryContext.class.st index d8cd7be..74b561f 100644 --- a/repository/Sentry-Core/SentryContext.class.st +++ b/repository/Sentry-Core/SentryContext.class.st @@ -1,96 +1,96 @@ -" -I hold all contextual information for the events. -" -Class { - #name : #SentryContext, - #superclass : #SentryBaseContext, - #instVars : [ - 'user', - 'request', - 'extra', - 'tags', - 'release' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #'accessing - special' } -SentryContext >> asDictionary [ - ^ self asDictionary: #() optional: #(user release request extra tags) -] - -{ #category : #initialization } -SentryContext >> clear [ - extra := {} asDictionary. - request := {} asDictionary. - user := {} asDictionary. - tags := {} asDictionary. - release := nil -] - -{ #category : #accessing } -SentryContext >> extra [ - ^ extra -] - -{ #category : #accessing } -SentryContext >> extra: aCollection [ - extra := aCollection asDictionary -] - -{ #category : #initialization } -SentryContext >> initialize [ - super initialize. - self clear -] - -{ #category : #merging } -SentryContext >> merge: aCollectionOrContext [ - aCollectionOrContext asDictionary - keysAndValuesDo: [ :key :value | - value isString - ifTrue: [ self perform: key asSymbol asMutator with: value ] - ifFalse: [ self perform: key asSymbol asMutator with: (self perform: key asSymbol) , value ] ] -] - -{ #category : #accessing } -SentryContext >> release [ - ^ release -] - -{ #category : #accessing } -SentryContext >> release: aRelease [ - release := aRelease -] - -{ #category : #accessing } -SentryContext >> request [ - ^ request -] - -{ #category : #accessing } -SentryContext >> request: aCollection [ - request := aCollection asDictionary -] - -{ #category : #accessing } -SentryContext >> tags [ - ^ tags -] - -{ #category : #accessing } -SentryContext >> tags: aCollection [ - tags := aCollection asDictionary -] - -{ #category : #accessing } -SentryContext >> user [ - ^ user -] - -{ #category : #accessing } -SentryContext >> user: aCollection [ - "the base attributes are id, email, ip_address, and username" - - user := aCollection asDictionary -] +" +I hold all contextual information for the events. +" +Class { + #name : #SentryContext, + #superclass : #SentryBaseContext, + #instVars : [ + 'user', + 'request', + 'extra', + 'tags', + 'release' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #'accessing - special' } +SentryContext >> asDictionary [ + ^ self asDictionary: #() optional: #(user release request extra tags) +] + +{ #category : #initialization } +SentryContext >> clear [ + extra := {} asDictionary. + request := {} asDictionary. + user := {} asDictionary. + tags := {} asDictionary. + release := nil +] + +{ #category : #accessing } +SentryContext >> extra [ + ^ extra +] + +{ #category : #accessing } +SentryContext >> extra: aCollection [ + extra := aCollection asDictionary +] + +{ #category : #initialization } +SentryContext >> initialize [ + super initialize. + self clear +] + +{ #category : #merging } +SentryContext >> merge: aCollectionOrContext [ + aCollectionOrContext asDictionary + keysAndValuesDo: [ :key :value | + value isString + ifTrue: [ self perform: key asSymbol asMutator with: value ] + ifFalse: [ self perform: key asSymbol asMutator with: (self perform: key asSymbol) , value ] ] +] + +{ #category : #accessing } +SentryContext >> release [ + ^ release +] + +{ #category : #accessing } +SentryContext >> release: aRelease [ + release := aRelease +] + +{ #category : #accessing } +SentryContext >> request [ + ^ request +] + +{ #category : #accessing } +SentryContext >> request: aCollection [ + request := aCollection asDictionary +] + +{ #category : #accessing } +SentryContext >> tags [ + ^ tags +] + +{ #category : #accessing } +SentryContext >> tags: aCollection [ + tags := aCollection asDictionary +] + +{ #category : #accessing } +SentryContext >> user [ + ^ user +] + +{ #category : #accessing } +SentryContext >> user: aCollection [ + "the base attributes are id, email, ip_address, and username" + + user := aCollection asDictionary +] diff --git a/repository/Sentry-Core/SentryConverter.class.st b/repository/Sentry-Core/SentryConverter.class.st index 18f7ac7..7e35ae6 100644 --- a/repository/Sentry-Core/SentryConverter.class.st +++ b/repository/Sentry-Core/SentryConverter.class.st @@ -1,45 +1,45 @@ -" -I take a Sentry data object (Event or an Interface) and produce a Dictionary. -" -Class { - #name : #SentryConverter, - #superclass : #Object, - #category : 'Sentry-Core-Utils' -} - -{ #category : #converting } -SentryConverter >> asDictionary: anObject keys: keys [ - | dict | - dict := Dictionary new. - keys - do: [ :each | - | value | - value := self read: anObject using: each. - self assert: [ value isNotNil ] description: each , ' is a mandatory argument.'. - dict at: each put: value ]. - ^ dict -] - -{ #category : #converting } -SentryConverter >> asDictionary: anObject keys: keys optional: optionalKeys [ - | dict | - dict := self asDictionary: anObject keys: keys. - optionalKeys - do: [ :each | - (self read: anObject using: each) - ifNotNil: [ :value | - (value isCollection and: [ value isEmpty ]) - ifFalse: [ dict at: each put: value ] ] ]. - ^ dict -] - -{ #category : #converting } -SentryConverter >> read: anObject using: aKey [ - | selector value | - selector := (((aKey splitOn: $_) collect: #capitalized) joinUsing: '') uncapitalized - asSymbol. - value := selector value: anObject. - ^ (value isKindOf: SentryInterface) - ifTrue: [ value asDictionary ] - ifFalse: [ value ] -] +" +I take a Sentry data object (Event or an Interface) and produce a Dictionary. +" +Class { + #name : #SentryConverter, + #superclass : #Object, + #category : 'Sentry-Core-Utils' +} + +{ #category : #converting } +SentryConverter >> asDictionary: anObject keys: keys [ + | dict | + dict := Dictionary new. + keys + do: [ :each | + | value | + value := self read: anObject using: each. + self assert: [ value isNotNil ] description: each , ' is a mandatory argument.'. + dict at: each put: value ]. + ^ dict +] + +{ #category : #converting } +SentryConverter >> asDictionary: anObject keys: keys optional: optionalKeys [ + | dict | + dict := self asDictionary: anObject keys: keys. + optionalKeys + do: [ :each | + (self read: anObject using: each) + ifNotNil: [ :value | + (value isCollection and: [ value isEmpty ]) + ifFalse: [ dict at: each put: value ] ] ]. + ^ dict +] + +{ #category : #converting } +SentryConverter >> read: anObject using: aKey [ + | selector value | + selector := (((aKey splitOn: $_) collect: #capitalized) joinUsing: '') uncapitalized + asSymbol. + value := selector value: anObject. + ^ (value isKindOf: SentryInterface) + ifTrue: [ value asDictionary ] + ifFalse: [ value ] +] diff --git a/repository/Sentry-Core/SentryDummyTransport.class.st b/repository/Sentry-Core/SentryDummyTransport.class.st index 78bc21d..009f469 100644 --- a/repository/Sentry-Core/SentryDummyTransport.class.st +++ b/repository/Sentry-Core/SentryDummyTransport.class.st @@ -1,24 +1,24 @@ -Class { - #name : #SentryDummyTransport, - #superclass : #SentryTransport, - #instVars : [ - 'onSend' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #initialization } -SentryDummyTransport >> initialize [ - super initialize. - onSend := [ :dict :dsn | ] -] - -{ #category : #accessing } -SentryDummyTransport >> onSend: aTwoArgBlock [ - onSend := aTwoArgBlock -] - -{ #category : #sending } -SentryDummyTransport >> send: aDictionary to: aDsn [ - onSend value: aDictionary value: aDsn -] +Class { + #name : #SentryDummyTransport, + #superclass : #SentryTransport, + #instVars : [ + 'onSend' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #initialization } +SentryDummyTransport >> initialize [ + super initialize. + onSend := [ :dict :dsn | ] +] + +{ #category : #accessing } +SentryDummyTransport >> onSend: aTwoArgBlock [ + onSend := aTwoArgBlock +] + +{ #category : #sending } +SentryDummyTransport >> send: aDictionary to: aDsn [ + onSend value: aDictionary value: aDsn +] diff --git a/repository/Sentry-Core/SentryEvent.class.st b/repository/Sentry-Core/SentryEvent.class.st index 33580ee..a8bc11d 100644 --- a/repository/Sentry-Core/SentryEvent.class.st +++ b/repository/Sentry-Core/SentryEvent.class.st @@ -1,265 +1,265 @@ -" -I represent a dispatched event. For available attributes see the sentry docs. - -I am serialized using `#asDictionary`. -" -Class { - #name : #SentryEvent, - #superclass : #Object, - #instVars : [ - 'eventId', - 'timestamp', - 'logger', - 'platform', - 'sdk', - 'level', - 'culprit', - 'serverName', - 'release', - 'tags', - 'environment', - 'modules', - 'extra', - 'fingerprint', - 'message', - 'exception', - 'contexts' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #'accessing - special' } -SentryEvent >> asDictionary [ - | dict | - dict := self requiredPayload - , - (SentryConverter new - asDictionary: self - keys: #(logger platform sdk) - optional: #(level contexts culprit serverName release tags environment modules extra fingerprint)). - dict - at: 'tags' - ifPresent: [ :t | - t isEmpty - ifTrue: [ dict removeKey: 'tags' ] ]. - self message - ifNotNil: [ :msg | - msg isString - ifTrue: [ dict at: 'message' put: msg ] - ifFalse: [ dict at: 'sentry.interfaces.Message' put: msg asDictionary ] ]. - self exception ifNotNil: [ :ex | dict at: 'exception' put: ex asDictionary ]. - ^ dict -] - -{ #category : #accessing } -SentryEvent >> contexts [ - ^ contexts -] - -{ #category : #accessing } -SentryEvent >> contexts: anObject [ - contexts := anObject -] - -{ #category : #accessing } -SentryEvent >> culprit [ - ^ culprit -] - -{ #category : #accessing } -SentryEvent >> culprit: anObject [ - culprit := anObject -] - -{ #category : #accessing } -SentryEvent >> environment [ - ^ environment -] - -{ #category : #accessing } -SentryEvent >> environment: anObject [ - environment := anObject -] - -{ #category : #accessing } -SentryEvent >> eventId [ - ^ eventId -] - -{ #category : #accessing } -SentryEvent >> eventId: anId [ - eventId := anId -] - -{ #category : #accessing } -SentryEvent >> eventIdString [ - ^ self uuid32For: self eventId -] - -{ #category : #accessing } -SentryEvent >> exception [ - ^ exception -] - -{ #category : #accessing } -SentryEvent >> exception: anObject [ - exception := anObject -] - -{ #category : #accessing } -SentryEvent >> extra [ - ^ extra -] - -{ #category : #accessing } -SentryEvent >> extra: anObject [ - extra := anObject -] - -{ #category : #accessing } -SentryEvent >> fingerprint [ - ^ fingerprint -] - -{ #category : #accessing } -SentryEvent >> fingerprint: anObject [ - fingerprint := anObject -] - -{ #category : #initialization } -SentryEvent >> initialize [ - super initialize. - eventId := UUID new. - timestamp := DateAndTime now. - logger := Sentry sdkName. - platform := 'other'. - sdk := {'name' -> Sentry sdkName. - 'version' -> Sentry sdkVersion} asDictionary. - contexts := {'runtime' - -> - {'name' -> 'Pharo'. - 'version' -> SystemVersion current highestUpdate asString} asDictionary} asDictionary. - tags := Dictionary new -] - -{ #category : #formatting } -SentryEvent >> isoTimestampFor: aDateTime [ - | utc | - utc := aDateTime asUTC. - ^ String - streamContents: [ :aStream | - utc printYMDOn: aStream withLeadingSpace: false. - aStream nextPut: $T. - utc printHMSOn: aStream ] -] - -{ #category : #accessing } -SentryEvent >> level [ - ^ level -] - -{ #category : #accessing } -SentryEvent >> level: anObject [ - level := anObject -] - -{ #category : #accessing } -SentryEvent >> logger [ - ^ logger -] - -{ #category : #accessing } -SentryEvent >> logger: anObject [ - logger := anObject -] - -{ #category : #accessing } -SentryEvent >> message [ - ^ message -] - -{ #category : #accessing } -SentryEvent >> message: anObject [ - message := anObject -] - -{ #category : #accessing } -SentryEvent >> modules [ - ^ modules -] - -{ #category : #accessing } -SentryEvent >> modules: anObject [ - modules := anObject -] - -{ #category : #accessing } -SentryEvent >> platform [ - ^ platform -] - -{ #category : #accessing } -SentryEvent >> platform: anObject [ - platform := anObject -] - -{ #category : #accessing } -SentryEvent >> release [ - ^ release -] - -{ #category : #accessing } -SentryEvent >> release: anObject [ - release := anObject -] - -{ #category : #'accessing - special' } -SentryEvent >> requiredPayload [ - ^ {'event_id' -> self eventIdString. - 'timestamp' -> (self isoTimestampFor: self timestamp)} asDictionary -] - -{ #category : #accessing } -SentryEvent >> sdk [ - ^ sdk -] - -{ #category : #accessing } -SentryEvent >> sdk: anObject [ - sdk := anObject -] - -{ #category : #accessing } -SentryEvent >> serverName [ - ^ serverName -] - -{ #category : #accessing } -SentryEvent >> serverName: anObject [ - serverName := anObject -] - -{ #category : #accessing } -SentryEvent >> tags [ - ^ tags -] - -{ #category : #accessing } -SentryEvent >> tags: anObject [ - tags := anObject -] - -{ #category : #accessing } -SentryEvent >> timestamp [ - ^ timestamp -] - -{ #category : #accessing } -SentryEvent >> timestamp: aDateTime [ - timestamp := aDateTime -] - -{ #category : #formatting } -SentryEvent >> uuid32For: aUUID [ - ^ aUUID asString copyWithout: $- -] +" +I represent a dispatched event. For available attributes see the sentry docs. + +I am serialized using `#asDictionary`. +" +Class { + #name : #SentryEvent, + #superclass : #Object, + #instVars : [ + 'eventId', + 'timestamp', + 'logger', + 'platform', + 'sdk', + 'level', + 'culprit', + 'serverName', + 'release', + 'tags', + 'environment', + 'modules', + 'extra', + 'fingerprint', + 'message', + 'exception', + 'contexts' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #'accessing - special' } +SentryEvent >> asDictionary [ + | dict | + dict := self requiredPayload + , + (SentryConverter new + asDictionary: self + keys: #(logger platform sdk) + optional: #(level contexts culprit serverName release tags environment modules extra fingerprint)). + dict + at: 'tags' + ifPresent: [ :t | + t isEmpty + ifTrue: [ dict removeKey: 'tags' ] ]. + self message + ifNotNil: [ :msg | + msg isString + ifTrue: [ dict at: 'message' put: msg ] + ifFalse: [ dict at: 'sentry.interfaces.Message' put: msg asDictionary ] ]. + self exception ifNotNil: [ :ex | dict at: 'exception' put: ex asDictionary ]. + ^ dict +] + +{ #category : #accessing } +SentryEvent >> contexts [ + ^ contexts +] + +{ #category : #accessing } +SentryEvent >> contexts: anObject [ + contexts := anObject +] + +{ #category : #accessing } +SentryEvent >> culprit [ + ^ culprit +] + +{ #category : #accessing } +SentryEvent >> culprit: anObject [ + culprit := anObject +] + +{ #category : #accessing } +SentryEvent >> environment [ + ^ environment +] + +{ #category : #accessing } +SentryEvent >> environment: anObject [ + environment := anObject +] + +{ #category : #accessing } +SentryEvent >> eventId [ + ^ eventId +] + +{ #category : #accessing } +SentryEvent >> eventId: anId [ + eventId := anId +] + +{ #category : #accessing } +SentryEvent >> eventIdString [ + ^ self uuid32For: self eventId +] + +{ #category : #accessing } +SentryEvent >> exception [ + ^ exception +] + +{ #category : #accessing } +SentryEvent >> exception: anObject [ + exception := anObject +] + +{ #category : #accessing } +SentryEvent >> extra [ + ^ extra +] + +{ #category : #accessing } +SentryEvent >> extra: anObject [ + extra := anObject +] + +{ #category : #accessing } +SentryEvent >> fingerprint [ + ^ fingerprint +] + +{ #category : #accessing } +SentryEvent >> fingerprint: anObject [ + fingerprint := anObject +] + +{ #category : #initialization } +SentryEvent >> initialize [ + super initialize. + eventId := UUID new. + timestamp := DateAndTime now. + logger := Sentry sdkName. + platform := 'other'. + sdk := {'name' -> Sentry sdkName. + 'version' -> Sentry sdkVersion} asDictionary. + contexts := {'runtime' + -> + {'name' -> 'Pharo'. + 'version' -> SystemVersion current highestUpdate asString} asDictionary} asDictionary. + tags := Dictionary new +] + +{ #category : #formatting } +SentryEvent >> isoTimestampFor: aDateTime [ + | utc | + utc := aDateTime asUTC. + ^ String + streamContents: [ :aStream | + utc printYMDOn: aStream withLeadingSpace: false. + aStream nextPut: $T. + utc printHMSOn: aStream ] +] + +{ #category : #accessing } +SentryEvent >> level [ + ^ level +] + +{ #category : #accessing } +SentryEvent >> level: anObject [ + level := anObject +] + +{ #category : #accessing } +SentryEvent >> logger [ + ^ logger +] + +{ #category : #accessing } +SentryEvent >> logger: anObject [ + logger := anObject +] + +{ #category : #accessing } +SentryEvent >> message [ + ^ message +] + +{ #category : #accessing } +SentryEvent >> message: anObject [ + message := anObject +] + +{ #category : #accessing } +SentryEvent >> modules [ + ^ modules +] + +{ #category : #accessing } +SentryEvent >> modules: anObject [ + modules := anObject +] + +{ #category : #accessing } +SentryEvent >> platform [ + ^ platform +] + +{ #category : #accessing } +SentryEvent >> platform: anObject [ + platform := anObject +] + +{ #category : #accessing } +SentryEvent >> release [ + ^ release +] + +{ #category : #accessing } +SentryEvent >> release: anObject [ + release := anObject +] + +{ #category : #'accessing - special' } +SentryEvent >> requiredPayload [ + ^ {'event_id' -> self eventIdString. + 'timestamp' -> (self isoTimestampFor: self timestamp)} asDictionary +] + +{ #category : #accessing } +SentryEvent >> sdk [ + ^ sdk +] + +{ #category : #accessing } +SentryEvent >> sdk: anObject [ + sdk := anObject +] + +{ #category : #accessing } +SentryEvent >> serverName [ + ^ serverName +] + +{ #category : #accessing } +SentryEvent >> serverName: anObject [ + serverName := anObject +] + +{ #category : #accessing } +SentryEvent >> tags [ + ^ tags +] + +{ #category : #accessing } +SentryEvent >> tags: anObject [ + tags := anObject +] + +{ #category : #accessing } +SentryEvent >> timestamp [ + ^ timestamp +] + +{ #category : #accessing } +SentryEvent >> timestamp: aDateTime [ + timestamp := aDateTime +] + +{ #category : #formatting } +SentryEvent >> uuid32For: aUUID [ + ^ aUUID asString copyWithout: $- +] diff --git a/repository/Sentry-Core/SentryException.class.st b/repository/Sentry-Core/SentryException.class.st index 587fa0c..78e9640 100644 --- a/repository/Sentry-Core/SentryException.class.st +++ b/repository/Sentry-Core/SentryException.class.st @@ -1,30 +1,30 @@ -Class { - #name : #SentryException, - #superclass : #SentryInterface, - #instVars : [ - 'values' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #accessing } -SentryException class >> value: aValue [ - ^ self new - values: {aValue}; - yourself -] - -{ #category : #converting } -SentryException >> asDictionary [ - ^ values collect: #asDictionary -] - -{ #category : #accessing } -SentryException >> values [ - ^ values -] - -{ #category : #accessing } -SentryException >> values: anObject [ - values := anObject -] +Class { + #name : #SentryException, + #superclass : #SentryInterface, + #instVars : [ + 'values' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #accessing } +SentryException class >> value: aValue [ + ^ self new + values: {aValue}; + yourself +] + +{ #category : #converting } +SentryException >> asDictionary [ + ^ values collect: #asDictionary +] + +{ #category : #accessing } +SentryException >> values [ + ^ values +] + +{ #category : #accessing } +SentryException >> values: anObject [ + values := anObject +] diff --git a/repository/Sentry-Core/SentryExceptionValue.class.st b/repository/Sentry-Core/SentryExceptionValue.class.st index 72ce0b0..7066f66 100644 --- a/repository/Sentry-Core/SentryExceptionValue.class.st +++ b/repository/Sentry-Core/SentryExceptionValue.class.st @@ -1,56 +1,56 @@ -Class { - #name : #SentryExceptionValue, - #superclass : #SentryInterface, - #instVars : [ - 'type', - 'value', - 'module', - 'stacktrace' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #converting } -SentryExceptionValue >> asDictionary [ - ^ super asDictionary: #(type value module) optional: #(stacktrace) -] - -{ #category : #accessing } -SentryExceptionValue >> module [ - ^ module -] - -{ #category : #accessing } -SentryExceptionValue >> module: anObject [ - module := anObject -] - -{ #category : #accessing } -SentryExceptionValue >> stacktrace [ - ^ stacktrace -] - -{ #category : #accessing } -SentryExceptionValue >> stacktrace: anObject [ - stacktrace := anObject -] - -{ #category : #accessing } -SentryExceptionValue >> type [ - ^ type -] - -{ #category : #accessing } -SentryExceptionValue >> type: anObject [ - type := anObject -] - -{ #category : #accessing } -SentryExceptionValue >> value [ - ^ value -] - -{ #category : #accessing } -SentryExceptionValue >> value: anObject [ - value := anObject -] +Class { + #name : #SentryExceptionValue, + #superclass : #SentryInterface, + #instVars : [ + 'type', + 'value', + 'module', + 'stacktrace' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #converting } +SentryExceptionValue >> asDictionary [ + ^ super asDictionary: #(type value module) optional: #(stacktrace) +] + +{ #category : #accessing } +SentryExceptionValue >> module [ + ^ module +] + +{ #category : #accessing } +SentryExceptionValue >> module: anObject [ + module := anObject +] + +{ #category : #accessing } +SentryExceptionValue >> stacktrace [ + ^ stacktrace +] + +{ #category : #accessing } +SentryExceptionValue >> stacktrace: anObject [ + stacktrace := anObject +] + +{ #category : #accessing } +SentryExceptionValue >> type [ + ^ type +] + +{ #category : #accessing } +SentryExceptionValue >> type: anObject [ + type := anObject +] + +{ #category : #accessing } +SentryExceptionValue >> value [ + ^ value +] + +{ #category : #accessing } +SentryExceptionValue >> value: anObject [ + value := anObject +] diff --git a/repository/Sentry-Core/SentryFrame.class.st b/repository/Sentry-Core/SentryFrame.class.st index 02c0a7a..a41ee4b 100644 --- a/repository/Sentry-Core/SentryFrame.class.st +++ b/repository/Sentry-Core/SentryFrame.class.st @@ -1,206 +1,207 @@ -Class { - #name : #SentryFrame, - #superclass : #SentryInterface, - #instVars : [ - 'filename', - 'function', - 'module', - 'lineno', - 'colno', - 'absPath', - 'contextLine', - 'preContext', - 'postContext', - 'inApp', - 'vars' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #accessing } -SentryFrame >> absPath [ - ^ absPath -] - -{ #category : #accessing } -SentryFrame >> absPath: anObject [ - absPath := anObject -] - -{ #category : #converting } -SentryFrame >> asDictionary [ - ^ self - asDictionary: #(filename function module) - optional: #(lineno colno abs_path context_line pre_context post_context in_app vars) -] - -{ #category : #'accessing - convenience' } -SentryFrame >> code: aSource start: aSelectionStart [ - ^ self code: aSource start: aSelectionStart contextSize: 5 -] - -{ #category : #'accessing - convenience' } -SentryFrame >> code: aSource start: aSelectionStart contextSize: aContextSize [ - | lines line | - lines := aSource lines. - line := aSource lineNumberCorrespondingToIndex: aSelectionStart. - self - lineno: line; - contextLine: (lines at: line); - preContext: (lines copyFrom: (1 max: line - aContextSize) to: line - 1); - postContext: (lines copyFrom: line + 1 to: (lines size min: line + aContextSize)) -] - -{ #category : #accessing } -SentryFrame >> colno [ - ^ colno -] - -{ #category : #accessing } -SentryFrame >> colno: anObject [ - colno := anObject -] - -{ #category : #accessing } -SentryFrame >> contextLine [ - ^ contextLine -] - -{ #category : #accessing } -SentryFrame >> contextLine: anObject [ - contextLine := anObject -] - -{ #category : #'accessing - derived' } -SentryFrame >> fileNameFor: aMethod [ - ^ self filetreeFileNameFor: aMethod -] - -{ #category : #accessing } -SentryFrame >> filename [ - ^ filename -] - -{ #category : #accessing } -SentryFrame >> filename: anObject [ - filename := anObject -] - -{ #category : #'accessing - convenience' } -SentryFrame >> filenameFromMethod: aMethod [ - self filename: (self fileNameFor: aMethod) -] - -{ #category : #'accessing - derived' } -SentryFrame >> filetreeFileNameFor: aMethod [ - ^ String - streamContents: [ :stream | - | methodClass | - methodClass := aMethod methodClass. - stream - << methodClass package name; - << '.package'; - << '/'. - stream - << methodClass theNonMetaClass name; - << '.class'; - << '/'. - methodClass isMeta - ifTrue: [ stream << 'class' ] - ifFalse: [ stream << 'instance' ]. - stream << '/'. - stream - << (MCFileTreeStCypressWriter fileNameForSelector: aMethod selector); - << '.st' ] -] - -{ #category : #accessing } -SentryFrame >> function [ - ^ function -] - -{ #category : #accessing } -SentryFrame >> function: anObject [ - function := anObject -] - -{ #category : #accessing } -SentryFrame >> inApp [ - ^ inApp -] - -{ #category : #accessing } -SentryFrame >> inApp: anObject [ - inApp := anObject -] - -{ #category : #accessing } -SentryFrame >> lineno [ - ^ lineno -] - -{ #category : #accessing } -SentryFrame >> lineno: anObject [ - lineno := anObject -] - -{ #category : #'accessing - convenience' } -SentryFrame >> method: aMethod [ - self filenameFromMethod: aMethod. - self function: aMethod selector. - self module: aMethod methodClass package name asString -] - -{ #category : #accessing } -SentryFrame >> module [ - ^ module -] - -{ #category : #accessing } -SentryFrame >> module: anObject [ - module := anObject -] - -{ #category : #accessing } -SentryFrame >> postContext [ - ^ postContext -] - -{ #category : #accessing } -SentryFrame >> postContext: anObject [ - postContext := anObject -] - -{ #category : #accessing } -SentryFrame >> preContext [ - ^ preContext -] - -{ #category : #accessing } -SentryFrame >> preContext: anObject [ - preContext := anObject -] - -{ #category : #'accessing - derived' } -SentryFrame >> tonelFileNameFor: aMethod [ - ^ String - streamContents: [ :stream | - | methodClass | - methodClass := aMethod methodClass. - stream - << methodClass package name; - << '/'. - stream - << methodClass theNonMetaClass name; - << '.class.st' ] -] - -{ #category : #accessing } -SentryFrame >> vars [ - ^ vars -] - -{ #category : #accessing } -SentryFrame >> vars: anObject [ - vars := anObject -] +Class { + #name : #SentryFrame, + #superclass : #SentryInterface, + #instVars : [ + 'filename', + 'function', + 'module', + 'lineno', + 'colno', + 'absPath', + 'contextLine', + 'preContext', + 'postContext', + 'inApp', + 'vars' + ], + #category : #'Sentry-Core-Model' +} + +{ #category : #accessing } +SentryFrame >> absPath [ + ^ absPath +] + +{ #category : #accessing } +SentryFrame >> absPath: anObject [ + absPath := anObject +] + +{ #category : #converting } +SentryFrame >> asDictionary [ + ^ self + asDictionary: #(filename function module) + optional: #(lineno colno abs_path context_line pre_context post_context in_app vars) +] + +{ #category : #'accessing - convenience' } +SentryFrame >> code: aSource start: aSelectionStart [ + ^ self code: aSource start: aSelectionStart contextSize: 5 +] + +{ #category : #'accessing - convenience' } +SentryFrame >> code: aSource start: aSelectionStart contextSize: aContextSize [ + | lines line | + lines := aSource lines. + line := aSource lineNumberCorrespondingToIndex: aSelectionStart. + self + lineno: line; + contextLine: (lines at: line); + preContext: (lines copyFrom: (1 max: line - aContextSize) to: line - 1); + postContext: (lines copyFrom: line + 1 to: (lines size min: line + aContextSize)) +] + +{ #category : #accessing } +SentryFrame >> colno [ + ^ colno +] + +{ #category : #accessing } +SentryFrame >> colno: anObject [ + colno := anObject +] + +{ #category : #accessing } +SentryFrame >> contextLine [ + ^ contextLine +] + +{ #category : #accessing } +SentryFrame >> contextLine: anObject [ + contextLine := anObject +] + +{ #category : #'accessing - derived' } +SentryFrame >> fileNameFor: aMethod [ + ^ self filetreeFileNameFor: aMethod +] + +{ #category : #accessing } +SentryFrame >> filename [ + ^ filename +] + +{ #category : #accessing } +SentryFrame >> filename: anObject [ + filename := anObject +] + +{ #category : #'accessing - convenience' } +SentryFrame >> filenameFromMethod: aMethod [ + self filename: (self fileNameFor: aMethod) +] + +{ #category : #'accessing - derived' } +SentryFrame >> filetreeFileNameFor: aMethod [ + + ^ String streamContents: [ :stream | + | methodClass | + methodClass := aMethod methodClass. + stream + << methodClass package name; + << '.package'; + << '/'. + stream + << methodClass instanceSide name; + << '.class'; + << '/'. + methodClass isMeta + ifTrue: [ stream << 'class' ] + ifFalse: [ stream << 'instance' ]. + stream << '/'. + stream + << + (MCFileTreeStCypressWriter fileNameForSelector: aMethod selector); + << '.st' ] +] + +{ #category : #accessing } +SentryFrame >> function [ + ^ function +] + +{ #category : #accessing } +SentryFrame >> function: anObject [ + function := anObject +] + +{ #category : #accessing } +SentryFrame >> inApp [ + ^ inApp +] + +{ #category : #accessing } +SentryFrame >> inApp: anObject [ + inApp := anObject +] + +{ #category : #accessing } +SentryFrame >> lineno [ + ^ lineno +] + +{ #category : #accessing } +SentryFrame >> lineno: anObject [ + lineno := anObject +] + +{ #category : #'accessing - convenience' } +SentryFrame >> method: aMethod [ + self filenameFromMethod: aMethod. + self function: aMethod selector. + self module: aMethod methodClass package name asString +] + +{ #category : #accessing } +SentryFrame >> module [ + ^ module +] + +{ #category : #accessing } +SentryFrame >> module: anObject [ + module := anObject +] + +{ #category : #accessing } +SentryFrame >> postContext [ + ^ postContext +] + +{ #category : #accessing } +SentryFrame >> postContext: anObject [ + postContext := anObject +] + +{ #category : #accessing } +SentryFrame >> preContext [ + ^ preContext +] + +{ #category : #accessing } +SentryFrame >> preContext: anObject [ + preContext := anObject +] + +{ #category : #'accessing - derived' } +SentryFrame >> tonelFileNameFor: aMethod [ + ^ String + streamContents: [ :stream | + | methodClass | + methodClass := aMethod methodClass. + stream + << methodClass package name; + << '/'. + stream + << methodClass theNonMetaClass name; + << '.class.st' ] +] + +{ #category : #accessing } +SentryFrame >> vars [ + ^ vars +] + +{ #category : #accessing } +SentryFrame >> vars: anObject [ + vars := anObject +] diff --git a/repository/Sentry-Core/SentryHttpTransport.class.st b/repository/Sentry-Core/SentryHttpTransport.class.st index 685cb57..9aecc6b 100644 --- a/repository/Sentry-Core/SentryHttpTransport.class.st +++ b/repository/Sentry-Core/SentryHttpTransport.class.st @@ -1,85 +1,85 @@ -Class { - #name : #SentryHttpTransport, - #superclass : #SentryTransport, - #instVars : [ - 'enableNotifications' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #accessing } -SentryHttpTransport >> authHeaderFor: aDsn [ - ^ {'Sentry sentry_version=7'. - 'sentry_timestamp=' , DateAndTime now asUnixTime asString. - 'sentry_key=' , aDsn username. - 'sentry_client=' , Sentry sdkName , '/' , Sentry sdkVersion} joinUsing: ',' -] - -{ #category : #initialization } -SentryHttpTransport >> initialize [ - super initialize. - enableNotifications := true -] - -{ #category : #sending } -SentryHttpTransport >> postClient: aZnClient [ - [ [ aZnClient post. - self processResponse: aZnClient response ] - on: NetworkError - do: [ :ex | self processException: ex ] ] fork -] - -{ #category : #sending } -SentryHttpTransport >> processException: anException [ - Sentry showNotification - ifFalse: [ ^ self ]. - (anException isKindOf: ConnectionTimedOut) - ifTrue: [ ^ self inform: 'Connecting to logging server timed out.' ]. - self inform: 'Logging the error to remote server failed (no connection?).' -] - -{ #category : #sending } -SentryHttpTransport >> processResponse: aResponse [ - Sentry showNotification & enableNotifications - ifFalse: [ ^ self ]. - aResponse isSuccess - ifTrue: [ ^ self inform: 'Error report sent.' ]. - self - inform: - 'Failed to send error report.' , String cr , '[' , aResponse code asString , '] ' - , (aResponse headers at: 'X-Sentry-Error' ifAbsent: [ '' ]) -] - -{ #category : #accessing } -SentryHttpTransport >> reportUrlFor: aDsn [ - ^ ZnUrl new - scheme: aDsn scheme; - host: aDsn host; - port: aDsn port; - segments: - {'api'. - aDsn path. - 'store'. - ''}; - yourself -] - -{ #category : #sending } -SentryHttpTransport >> send: aDictionary to: aDsn [ - enableNotifications := aDictionary includesKey: 'exception'. - self - postClient: - (ZnClient new - beOneShot; - headerAt: 'User-Agent' put: self userAgent; - headerAt: 'X-Sentry-Auth' put: (self authHeaderFor: aDsn); - accept: ZnMimeType applicationJson; - url: (self reportUrlFor: aDsn); - entity: (ZnEntity with: (STON toJsonString: aDictionary) type: ZnMimeType applicationJson); - yourself) -] - -{ #category : #accessing } -SentryHttpTransport >> userAgent [ - ^ Sentry sdkName , '/' , Sentry sdkVersion -] +Class { + #name : #SentryHttpTransport, + #superclass : #SentryTransport, + #instVars : [ + 'enableNotifications' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #accessing } +SentryHttpTransport >> authHeaderFor: aDsn [ + ^ {'Sentry sentry_version=7'. + 'sentry_timestamp=' , DateAndTime now asUnixTime asString. + 'sentry_key=' , aDsn username. + 'sentry_client=' , Sentry sdkName , '/' , Sentry sdkVersion} joinUsing: ',' +] + +{ #category : #initialization } +SentryHttpTransport >> initialize [ + super initialize. + enableNotifications := true +] + +{ #category : #sending } +SentryHttpTransport >> postClient: aZnClient [ + [ [ aZnClient post. + self processResponse: aZnClient response ] + on: NetworkError + do: [ :ex | self processException: ex ] ] fork +] + +{ #category : #sending } +SentryHttpTransport >> processException: anException [ + Sentry showNotification + ifFalse: [ ^ self ]. + (anException isKindOf: ConnectionTimedOut) + ifTrue: [ ^ self inform: 'Connecting to logging server timed out.' ]. + self inform: 'Logging the error to remote server failed (no connection?).' +] + +{ #category : #sending } +SentryHttpTransport >> processResponse: aResponse [ + Sentry showNotification & enableNotifications + ifFalse: [ ^ self ]. + aResponse isSuccess + ifTrue: [ ^ self inform: 'Error report sent.' ]. + self + inform: + 'Failed to send error report.' , String cr , '[' , aResponse code asString , '] ' + , (aResponse headers at: 'X-Sentry-Error' ifAbsent: [ '' ]) +] + +{ #category : #accessing } +SentryHttpTransport >> reportUrlFor: aDsn [ + ^ ZnUrl new + scheme: aDsn scheme; + host: aDsn host; + port: aDsn port; + segments: + {'api'. + aDsn path. + 'store'. + ''}; + yourself +] + +{ #category : #sending } +SentryHttpTransport >> send: aDictionary to: aDsn [ + enableNotifications := aDictionary includesKey: 'exception'. + self + postClient: + (ZnClient new + beOneShot; + headerAt: 'User-Agent' put: self userAgent; + headerAt: 'X-Sentry-Auth' put: (self authHeaderFor: aDsn); + accept: ZnMimeType applicationJson; + url: (self reportUrlFor: aDsn); + entity: (ZnEntity with: (STON toJsonString: aDictionary) type: ZnMimeType applicationJson); + yourself) +] + +{ #category : #accessing } +SentryHttpTransport >> userAgent [ + ^ Sentry sdkName , '/' , Sentry sdkVersion +] diff --git a/repository/Sentry-Core/SentryInterface.class.st b/repository/Sentry-Core/SentryInterface.class.st index f724bee..b4cc610 100644 --- a/repository/Sentry-Core/SentryInterface.class.st +++ b/repository/Sentry-Core/SentryInterface.class.st @@ -1,20 +1,20 @@ -Class { - #name : #SentryInterface, - #superclass : #Object, - #category : 'Sentry-Core-Model' -} - -{ #category : #converting } -SentryInterface >> asDictionary [ - ^ self subclassResponsibility -] - -{ #category : #converting } -SentryInterface >> asDictionary: keys [ - ^ SentryConverter new asDictionary: self keys: keys -] - -{ #category : #converting } -SentryInterface >> asDictionary: keys optional: optionalKeys [ - ^ SentryConverter new asDictionary: self keys: keys optional: optionalKeys -] +Class { + #name : #SentryInterface, + #superclass : #Object, + #category : 'Sentry-Core-Model' +} + +{ #category : #converting } +SentryInterface >> asDictionary [ + ^ self subclassResponsibility +] + +{ #category : #converting } +SentryInterface >> asDictionary: keys [ + ^ SentryConverter new asDictionary: self keys: keys +] + +{ #category : #converting } +SentryInterface >> asDictionary: keys optional: optionalKeys [ + ^ SentryConverter new asDictionary: self keys: keys optional: optionalKeys +] diff --git a/repository/Sentry-Core/SentryMessage.class.st b/repository/Sentry-Core/SentryMessage.class.st index 7279b14..a38779e 100644 --- a/repository/Sentry-Core/SentryMessage.class.st +++ b/repository/Sentry-Core/SentryMessage.class.st @@ -1,60 +1,60 @@ -Class { - #name : #SentryMessage, - #superclass : #SentryInterface, - #instVars : [ - 'message', - 'params', - 'formatted' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #'instance creation' } -SentryMessage class >> message: aString [ - ^ self new - message: aString; - yourself -] - -{ #category : #'instance creation' } -SentryMessage class >> message: aString params: aCollection [ - ^ self new - message: aString; - params: aCollection; - yourself -] - -{ #category : #converting } -SentryMessage >> asDictionary [ - ^ self asDictionary: #(message) optional: #(params formatted) -] - -{ #category : #accessing } -SentryMessage >> formatted [ - ^ formatted -] - -{ #category : #accessing } -SentryMessage >> formatted: anObject [ - formatted := anObject -] - -{ #category : #accessing } -SentryMessage >> message [ - ^ message -] - -{ #category : #accessing } -SentryMessage >> message: anObject [ - message := anObject -] - -{ #category : #accessing } -SentryMessage >> params [ - ^ params asArray -] - -{ #category : #accessing } -SentryMessage >> params: anObject [ - params := anObject -] +Class { + #name : #SentryMessage, + #superclass : #SentryInterface, + #instVars : [ + 'message', + 'params', + 'formatted' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #'instance creation' } +SentryMessage class >> message: aString [ + ^ self new + message: aString; + yourself +] + +{ #category : #'instance creation' } +SentryMessage class >> message: aString params: aCollection [ + ^ self new + message: aString; + params: aCollection; + yourself +] + +{ #category : #converting } +SentryMessage >> asDictionary [ + ^ self asDictionary: #(message) optional: #(params formatted) +] + +{ #category : #accessing } +SentryMessage >> formatted [ + ^ formatted +] + +{ #category : #accessing } +SentryMessage >> formatted: anObject [ + formatted := anObject +] + +{ #category : #accessing } +SentryMessage >> message [ + ^ message +] + +{ #category : #accessing } +SentryMessage >> message: anObject [ + message := anObject +] + +{ #category : #accessing } +SentryMessage >> params [ + ^ params asArray +] + +{ #category : #accessing } +SentryMessage >> params: anObject [ + params := anObject +] diff --git a/repository/Sentry-Core/SentryStacktrace.class.st b/repository/Sentry-Core/SentryStacktrace.class.st index bcc26d2..c39765c 100644 --- a/repository/Sentry-Core/SentryStacktrace.class.st +++ b/repository/Sentry-Core/SentryStacktrace.class.st @@ -1,35 +1,35 @@ -Class { - #name : #SentryStacktrace, - #superclass : #SentryInterface, - #instVars : [ - 'frames', - 'framesOmitted' - ], - #category : 'Sentry-Core-Model' -} - -{ #category : #converting } -SentryStacktrace >> asDictionary [ - ^ {'frames' -> (self frames collect: #asDictionary)} asDictionary - , (self asDictionary: #() optional: #(frames_omitted)) -] - -{ #category : #accessing } -SentryStacktrace >> frames [ - ^ frames -] - -{ #category : #accessing } -SentryStacktrace >> frames: anObject [ - frames := anObject -] - -{ #category : #accessing } -SentryStacktrace >> framesOmitted [ - ^ framesOmitted -] - -{ #category : #accessing } -SentryStacktrace >> framesOmitted: anObject [ - framesOmitted := anObject -] +Class { + #name : #SentryStacktrace, + #superclass : #SentryInterface, + #instVars : [ + 'frames', + 'framesOmitted' + ], + #category : 'Sentry-Core-Model' +} + +{ #category : #converting } +SentryStacktrace >> asDictionary [ + ^ {'frames' -> (self frames collect: #asDictionary)} asDictionary + , (self asDictionary: #() optional: #(frames_omitted)) +] + +{ #category : #accessing } +SentryStacktrace >> frames [ + ^ frames +] + +{ #category : #accessing } +SentryStacktrace >> frames: anObject [ + frames := anObject +] + +{ #category : #accessing } +SentryStacktrace >> framesOmitted [ + ^ framesOmitted +] + +{ #category : #accessing } +SentryStacktrace >> framesOmitted: anObject [ + framesOmitted := anObject +] diff --git a/repository/Sentry-Core/SentryTransport.class.st b/repository/Sentry-Core/SentryTransport.class.st index 1373c01..553dc37 100644 --- a/repository/Sentry-Core/SentryTransport.class.st +++ b/repository/Sentry-Core/SentryTransport.class.st @@ -1,10 +1,10 @@ -Class { - #name : #SentryTransport, - #superclass : #Object, - #category : 'Sentry-Core-Model' -} - -{ #category : #sending } -SentryTransport >> send: aDictionary to: aDsn [ - self subclassResponsibility -] +Class { + #name : #SentryTransport, + #superclass : #Object, + #category : 'Sentry-Core-Model' +} + +{ #category : #sending } +SentryTransport >> send: aDictionary to: aDsn [ + self subclassResponsibility +] diff --git a/repository/Sentry-Core/package.st b/repository/Sentry-Core/package.st index d714adb..60223f1 100644 --- a/repository/Sentry-Core/package.st +++ b/repository/Sentry-Core/package.st @@ -1 +1 @@ -Package { #name : #'Sentry-Core' } +Package { #name : #'Sentry-Core' }