@@ -8,118 +8,121 @@ import OpenTelemetrySdk
88
99// protocol for exporters that can be decorated with `PersistenceExporterDecorator`
1010protocol DecoratedExporter {
11- associatedtype SignalType
11+ associatedtype SignalType
1212
13- func export( values: [ SignalType ] ) -> DataExportStatus
13+ func export( values: [ SignalType ] ) -> DataExportStatus
1414}
1515
1616// a generic decorator of `DecoratedExporter` adding filesystem persistence of batches of `[T.SignalType]`.
1717// `T.SignalType` must conform to `Codable`.
1818internal class PersistenceExporterDecorator < T> where T: DecoratedExporter , T. SignalType: Codable {
19- // a wrapper of `DecoratedExporter` (T) to add conformance to `DataExporter` that can be
20- // used with `DataExportWorker`.
21- private class DecoratedDataExporter : DataExporter {
22- private let decoratedExporter : T
23-
24- init ( decoratedExporter: T ) {
25- self . decoratedExporter = decoratedExporter
26- }
27-
28- func export( data: Data ) -> DataExportStatus {
29- // decode batches of `[T.SignalType]` from the raw data.
30- // the data is made of batches of comma-suffixed JSON arrays, so in order to utilize
31- // `JSONDecoder`, add a "[" prefix and "null]" suffix making the data a valid
32- // JSON array of `[T.SignalType]`.
33- var arrayData : Data = JSONDataConstants . arrayPrefix
34- arrayData. append ( data)
35- arrayData. append ( JSONDataConstants . arraySuffix)
36-
37- do {
38- let decoder = JSONDecoder ( )
39- let exportables = try decoder. decode (
40- [ [ T . SignalType ] ? ] . self,
41- from: arrayData
42- ) . compactMap { $0 } . flatMap { $0 }
43-
44- return decoratedExporter. export ( values: exportables)
45- } catch {
46- return DataExportStatus ( needsRetry: false )
47- }
48- }
49- }
50-
51- private let performancePreset : PersistencePerformancePreset
52-
53- private let fileWriter : FileWriter
54-
55- private let worker : DataExportWorkerProtocol
56-
57- public convenience init ( decoratedExporter: T ,
58- storageURL: URL ,
59- exportCondition: @escaping ( ) -> Bool = { true } ,
60- performancePreset: PersistencePerformancePreset = . default)
61- {
62- // orchestrate writes and reads over the folder given by `storageURL`
63- let filesOrchestrator = FilesOrchestrator (
64- directory: Directory ( url: storageURL) ,
65- performance: performancePreset,
66- dateProvider: SystemDateProvider ( )
67- )
68-
69- let fileWriter = OrchestratedFileWriter (
70- orchestrator: filesOrchestrator
71- )
19+ // a wrapper of `DecoratedExporter` (T) to add conformance to `DataExporter` that can be
20+ // used with `DataExportWorker`.
21+ private class DecoratedDataExporter : DataExporter {
22+ private let decoratedExporter : T
7223
73- let fileReader = OrchestratedFileReader (
74- orchestrator: filesOrchestrator
75- )
76-
77- self . init ( decoratedExporter: decoratedExporter,
78- fileWriter: fileWriter,
79- workerFactory: {
80- DataExportWorker (
81- fileReader: fileReader,
82- dataExporter: $0,
83- exportCondition: exportCondition,
84- delay: DataExportDelay ( performance: performancePreset)
85- )
86- } ,
87- performancePreset: performancePreset)
24+ init ( decoratedExporter: T ) {
25+ self . decoratedExporter = decoratedExporter
8826 }
8927
90- // internal initializer for testing that accepts a worker factory that allows mocking the worker
91- internal init ( decoratedExporter: T ,
92- fileWriter: FileWriter ,
93- workerFactory createWorker: ( DataExporter ) -> DataExportWorkerProtocol ,
94- performancePreset: PersistencePerformancePreset )
95- {
96- self . performancePreset = performancePreset
97-
98- self . fileWriter = fileWriter
99-
100- self . worker = createWorker ( DecoratedDataExporter ( decoratedExporter: decoratedExporter) )
28+ func export( data: Data ) -> DataExportStatus {
29+ // decode batches of `[T.SignalType]` from the raw data.
30+ // the data is made of batches of comma-suffixed JSON arrays, so in order to utilize
31+ // `JSONDecoder`, add a "[" prefix and "null]" suffix making the data a valid
32+ // JSON array of `[T.SignalType]`.
33+ var arrayData : Data = JSONDataConstants . arrayPrefix
34+ arrayData. append ( data)
35+ arrayData. append ( JSONDataConstants . arraySuffix)
36+
37+ do {
38+ let decoder = JSONDecoder ( )
39+ let exportables = try decoder. decode (
40+ [ [ T . SignalType ] ? ] . self,
41+ from: arrayData
42+ ) . compactMap { $0 } . flatMap { $0 }
43+
44+ return decoratedExporter. export ( values: exportables)
45+ } catch {
46+ return DataExportStatus ( needsRetry: false )
47+ }
10148 }
102-
103- public func export( values: [ T . SignalType ] ) throws {
104- let encoder = JSONEncoder ( )
105- var data = try encoder. encode ( values)
106- data. append ( JSONDataConstants . arraySeparator)
107-
108- if performancePreset. synchronousWrite {
109- fileWriter. writeSync ( data: data)
110- } else {
111- fileWriter. write ( data: data)
112- }
49+ }
50+
51+ private let performancePreset : PersistencePerformancePreset
52+
53+ private let fileWriter : FileWriter
54+
55+ private let worker : DataExportWorkerProtocol
56+
57+ public convenience init (
58+ decoratedExporter: T ,
59+ storageURL: URL ,
60+ exportCondition: @escaping ( ) -> Bool = { true } ,
61+ performancePreset: PersistencePerformancePreset = . default
62+ ) {
63+ // orchestrate writes and reads over the folder given by `storageURL`
64+ let filesOrchestrator = FilesOrchestrator (
65+ directory: Directory ( url: storageURL) ,
66+ performance: performancePreset,
67+ dateProvider: SystemDateProvider ( )
68+ )
69+
70+ let fileWriter = OrchestratedFileWriter (
71+ orchestrator: filesOrchestrator
72+ )
73+
74+ let fileReader = OrchestratedFileReader (
75+ orchestrator: filesOrchestrator
76+ )
77+
78+ self . init (
79+ decoratedExporter: decoratedExporter,
80+ fileWriter: fileWriter,
81+ workerFactory: {
82+ DataExportWorker (
83+ fileReader: fileReader,
84+ dataExporter: $0,
85+ exportCondition: exportCondition,
86+ delay: DataExportDelay ( performance: performancePreset)
87+ )
88+ } ,
89+ performancePreset: performancePreset)
90+ }
91+
92+ // internal initializer for testing that accepts a worker factory that allows mocking the worker
93+ internal init (
94+ decoratedExporter: T ,
95+ fileWriter: FileWriter ,
96+ workerFactory createWorker: ( DataExporter ) -> DataExportWorkerProtocol ,
97+ performancePreset: PersistencePerformancePreset
98+ ) {
99+ self . performancePreset = performancePreset
100+
101+ self . fileWriter = fileWriter
102+
103+ self . worker = createWorker ( DecoratedDataExporter ( decoratedExporter: decoratedExporter) )
104+ }
105+
106+ public func export( values: [ T . SignalType ] ) throws {
107+ let encoder = JSONEncoder ( )
108+ var data = try encoder. encode ( values)
109+ data. append ( JSONDataConstants . arraySeparator)
110+
111+ if performancePreset. synchronousWrite {
112+ fileWriter. writeSync ( data: data)
113+ } else {
114+ fileWriter. write ( data: data)
113115 }
116+ }
114117
115- public func flush( ) {
116- fileWriter. flush ( )
117- _ = worker. flush ( )
118- }
118+ public func flush( ) {
119+ fileWriter. flush ( )
120+ _ = worker. flush ( )
121+ }
119122}
120123
121124private enum JSONDataConstants {
122- static let arrayPrefix = " [ " . data ( using: . utf8) !
123- static let arraySuffix = " null] " . data ( using: . utf8) !
124- static let arraySeparator = " , " . data ( using: . utf8) !
125+ static let arrayPrefix = " [ " . data ( using: . utf8) !
126+ static let arraySuffix = " null] " . data ( using: . utf8) !
127+ static let arraySeparator = " , " . data ( using: . utf8) !
125128}
0 commit comments