@@ -103,26 +103,21 @@ extension LoggerMiddleware {
103103// MARK: State Logger
104104extension LoggerMiddleware {
105105 public enum StateLogger {
106+ /// Logs using os_log.
106107 case osLog
107- case file( URL )
108+ /// Appends the messages to a file. The file must exist!
109+ case file( FileAppender )
110+ /// A custom handler.
108111 case custom( ( String ) -> Void )
109112
110113 func log( state: String ) {
111114 switch self {
112- case . osLog: LoggerMiddleware . osLog ( state: state )
113- case let . file( url ) : LoggerMiddleware . fileLog ( state: state , to : url )
115+ case . osLog: LoggerMiddleware . osLog ( state)
116+ case let . file( fileAppender ) : fileAppender . write ( state)
114117 case let . custom( closure) : closure ( state)
115118 }
116119 }
117120 }
118-
119- private static func osLog( state: String ) {
120- os_log ( . debug, log: . default, " %{PUBLIC}@ " , state)
121- }
122-
123- private static func fileLog( state: String , to fileURL: URL ) {
124- try ? state. write ( toFile: fileURL. absoluteString, atomically: false , encoding: String . Encoding. utf8)
125- }
126121}
127122
128123// MARK: State Diff Transform
@@ -243,32 +238,27 @@ extension LoggerMiddleware {
243238// MARK: Action Logger
244239extension LoggerMiddleware {
245240 public enum ActionLogger {
241+ /// Logs using os_log.
246242 case osLog
247- case file( URL )
243+ /// Appends the messages to a file. The file must exist!
244+ case file( FileAppender )
245+ /// A custom handler.
248246 case custom( ( String ) -> Void )
249247
250248 func log( action: String ) {
251249 switch self {
252- case . osLog: LoggerMiddleware . osLog ( action: action )
253- case let . file( url ) : LoggerMiddleware . fileLog ( action: action , to : url )
250+ case . osLog: LoggerMiddleware . osLog ( action)
251+ case let . file( fileappender ) : fileappender . write ( action)
254252 case let . custom( closure) : closure ( action)
255253 }
256254 }
257255 }
258-
259- private static func osLog( action: String ) {
260- os_log ( . debug, log: . default, " %{PUBLIC}@ " , action)
261- }
262-
263- private static func fileLog( action: String , to fileURL: URL ) -> Void {
264- try ? action. write ( toFile: fileURL. absoluteString, atomically: false , encoding: . utf8)
265- }
266256}
267257
268258// MARK: Action Transform
269259extension LoggerMiddleware {
270260 public enum ActionTransform {
271- case `default`( actionPrefix: String = " \n 🕹 " , sourcePrefix: String = " \n 🎪 " )
261+ case `default`( actionPrefix: String = " 🕹 " , sourcePrefix: String = " 🎪 " )
272262 case actionNameOnly
273263 case custom( ( InputActionType , ActionSource ) -> String )
274264
@@ -284,3 +274,49 @@ extension LoggerMiddleware {
284274 }
285275 }
286276}
277+
278+ // MARK: Log output
279+
280+ extension LoggerMiddleware {
281+
282+ fileprivate static func osLog( _ message: String ) {
283+ os_log ( . debug, log: . default, " %{PUBLIC}@ " , message)
284+ }
285+
286+ }
287+
288+ public struct FileAppender {
289+ private let url : URL
290+ private let date : ( ) -> Date
291+ private let dateFormatter : DateFormatter
292+ private let writer : ( URL , Data ) -> Void
293+
294+ public init ( url: URL , date: @escaping ( ) -> Date , dateFormatter: DateFormatter , writer: @escaping ( URL , Data ) -> Void ) {
295+ self . url = url
296+ self . date = date
297+ self . dateFormatter = dateFormatter
298+ self . writer = writer
299+ }
300+
301+ public func write( _ message: String ) {
302+ guard let data = ( dateFormatter. string ( from: date ( ) ) + " " + message + " \n " ) . data ( using: String . Encoding. utf8) else { return }
303+ writer ( url, data)
304+ }
305+ }
306+
307+ extension FileAppender {
308+ public static func live( url: URL , dateFormatter: DateFormatter = . init( ) , date: @escaping ( ) -> Date = Date . init, fileHandle: @escaping ( URL ) throws -> FileHandle = FileHandle . init ( forUpdating: ) ) -> FileAppender {
309+ FileAppender (
310+ url: url,
311+ date: date,
312+ dateFormatter: dateFormatter,
313+ writer: { url, data in
314+ guard let fileUpdater = try ? fileHandle ( url) else { return }
315+ fileUpdater. seekToEndOfFile ( )
316+ fileUpdater. write ( data)
317+ fileUpdater. closeFile ( )
318+ }
319+ )
320+ }
321+ }
322+
0 commit comments