-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added a Logger that can log messages in different levels. #156
Conversation
@@ -43,6 +43,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { | |||
return | |||
} | |||
PiwikTracker.configureSharedInstance(withSiteID: siteid, baseURL: baseurl) | |||
PiwikTracker.shared?.logger?.minLevel = .info |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it is not purpose of this PR but,
I would really suggest staying away from singleton and shared instances.
A design goal would be to run multiple trackers instances in the same app.
Let the consumer of the framework create their own shared instance if they want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe create an issue for tracking this refactoring if we think it is a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a ticket (#118) to ensure the Tracker can be instantiated multiple times and I agree that it is necessary to do so. But I don't think we should remove the default, shared logger. I think most users just use a single tracker and are used to have a shared instance. At the same time it doesn't break any other features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @brototyp , I also think that users would like to have a shared instance to as a convenient way to use a logger. More ambitious users can use their own instances of loggers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My experience from looking at a lot of iOS/Mac code is a pattern of overusing singletons. I am just trying to avoid adding to this problem :)
I personally do not see any reason for the framework to provide a singleton. A user that really want to use the singleton pattern in their code can just declare a public variable in the AppDelegate
or introduce a Tracker
class with a shared variable or a global variable.
Up to you. I am just on a personal crusade to reduce the use of singletons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe look at Alamofire if you want to offer a "default" instance without having to instantiate one.
@@ -0,0 +1,55 @@ | |||
import Foundation | |||
|
|||
public enum LogLevel: Int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it have to be public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comment below.
PiwikTracker/Logger.swift
Outdated
} | ||
} | ||
|
||
public protocol Logger { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it have to be public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comment below.
PiwikTracker/Logger.swift
Outdated
|
||
public protocol Logger { | ||
var minLevel: LogLevel { get set } | ||
func log(message: ()->(String), with level: LogLevel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could capture file, function line number in the logs
func log(_ message: Void -> String, with level: LogLevel, file: String = #file, function: String = #function, line: Int = #line )
the extension methods below also needs the same params for this to work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love it! Good idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just changed this. It's great!
PiwikTracker/Logger.swift
Outdated
} | ||
} | ||
|
||
final class DefaultLogger: Logger { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would consider making this a struct.
But final class
is almost as good :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather like to use structs for data structures whose main task is to hold data. For things like this, where the main task is to "do things" or "define behavior" I like to use classes. What do you think about this?
PiwikTracker/PiwikTracker.swift
Outdated
@@ -22,6 +22,8 @@ final public class PiwikTracker: NSObject { | |||
private var queue: Queue | |||
internal let siteId: String | |||
|
|||
public var logger: Logger? = DefaultLogger(minLevel: .warning) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably make this non optional and not expose the Logger implementations outside the framework.
If the consumer of the framework needs to change the logging level I would hide that behind a method on the PiwikTracker class, e.g. enableDebugLogging that creates and assign new immutable DefaultLogger instance with an appropriate log level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comment below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still suggest making the logger non optional even if exposing it as a property.
The code will become more easy to write and reason about and the user will be explicitly forced to provide a value.
You can provide a default implementation of the ´NoOpLogger´ that user can set to make it easy for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created a DisabledLogger
.
PiwikTracker/PiwikTracker.swift
Outdated
@@ -22,6 +22,8 @@ final public class PiwikTracker: NSObject { | |||
private var queue: Queue | |||
internal let siteId: String | |||
|
|||
public var logger: Logger? = DefaultLogger(minLevel: .warning) | |||
|
|||
internal static var _sharedInstance: PiwikTracker? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think _
prefixing is a thing of the past but accept if someone else thinks otherwise :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think omitting the underscore would be best?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not use '_' in any variable or instance names.
Often if you find yourself facing problems with naming resorting to using _
or other type of prefix/post fix to avoid naming conflicts it is an indication of a design problem.
PiwikTracker/PiwikTracker.swift
Outdated
@@ -55,6 +57,7 @@ final public class PiwikTracker: NSObject { | |||
|
|||
internal func queue(event: Event) { | |||
guard !isOptedOut else { return } | |||
logger?.verbose(){ return "Queued event: \(event)" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would
logger?.verbose { "Queued event: \(event)" }
work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func verbose(_ message: @autoclosure ()->(String)) { ... }
will allow and even conciser syntax
verbose("123")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! I didn't know about the @autoclosure
. It's perfect for this use case. Will add it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just changed this. It's great!
Podfile.lock
Outdated
@@ -1,8 +1,8 @@ | |||
PODS: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure the .lock file should be tracked by Git.
Maybe add it to .gitignore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I like to track the .lock file to define which exact versions this is ensured to work with. In this case, since there are only dependencies for the tests, I agree.
One core feature I wanted to go for in this implementation is a logger that can be replaced. So one could write an adapter for their own Logger (Cocoalumberjack, XCGLogger, ...). That's why the Logger protocol and LogLevel enum as well as there logger property of the Tracker are public. |
4a177ce
to
cd80305
Compare
Ok, I understand, maybe a good idea. I just think logging is not a core feature for a consumer of the framework. I almost think the framework should never log anything when used in production. Any information that needs to reach the consumer should be a return values or exception. Logging in the context of this framework is only for developers of the framework it self. This will force the developer of the framework to think about the design. But I understand the purpose and it is ok. |
PiwikTracker/Logger.swift
Outdated
|
||
func log(_ message: @autoclosure () -> String, with level: LogLevel, file: String = #file, function: String = #function, line: Int = #line) { | ||
guard level.rawValue >= minLevel.rawValue else { return } | ||
print("PiwikTracker [\(level.shortcut)] \(message())") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot about this.
print
is not thread safe (I think).
If this method is called from different thread the print statements will be jumbled up.
You can solve that by doing the printing on a serial queue.
If you do that just must be careful about running message()
on the calling thread before switching to a serial queue or you might get crashes depending on what the provided closure does.
You should probably add a comment on the Logger
protocol that the implementation needs to deal with any threading themselves. Of instead of exposing the Logger
to the consumer expose an Appender
the consumer can set on the Logger
and let Logger
handle the threading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right! Thanks for pointing this out!
cd80305
to
71ffe7a
Compare
Closes : #147