Skip to content

Commit

Permalink
Update documentaion.
Browse files Browse the repository at this point in the history
  • Loading branch information
tonystone committed Feb 28, 2019
1 parent 02f26e0 commit 1ad3b00
Show file tree
Hide file tree
Showing 18 changed files with 437 additions and 292 deletions.
1 change: 1 addition & 0 deletions Documentation/Abstracts/User Guides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use the guides below to help you utilize TraceLog fully.
125 changes: 125 additions & 0 deletions Documentation/Configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Configuration

TraceLog can be configured via the environment either manually using `export` or via Xcode.
For TraceLog to read the environment on startup, you must call its configure method at the beginning of your application.

```swift
TraceLog.configure()
```

## Log Writers

TraceLog can be configured with multiple custom log writers who do the job of outputting the log statements to the desired location. By default, it configures itself with a `ConsoleWriter`
which outputs to `stdout`. You can change the writers at any time and chain multiple of them to output to different locations such as to a remote logging servers, syslog, etc.

Writers must implement the `Writer` protocol. To install them, simply call configure with an array of `Writers`.

```swift
let networkWriter = NetworkWriter(url: URL("http://mydomain.com/log"))

TraceLog.configure(writers: [ConsoleWriter(), networkWriter])
```

Since writers are instantiated before the call, you are free to initialize them with whatever makes sense for the writer type to be created. For instance in the case of the network writer the URL of
the logging endpoint.

## Concurrency Modes

TraceLog can be configured to run in 3 main concurrency modes, `.direct`, `.sync`, or `.async`. These modes determine how TraceLog will invoke each writer as it logs your logging statements.

* `.direct` - Direct, as the name implies, will directly call the writer from the calling thread with no indirection. It will block until the writer(s) in this mode have completed the write to the endpoint. Useful for scripting applications and other applications where it is required for the call not to return until the message is printed.

* `.sync` - Synchronous blocking mode is similar to direct in that it blocks but this mode also uses a queue for all writes. The benefits of that is that all threads writing to the log will be serialized through before calling the writer (one call to the writer at a time).

* `.async` - Asynchronous non-blocking mode. A general mode used for most application which queues all messages before being evaluated or logged. This ensures minimal delays in application execution due to logging.

Modes can be configured globally for all writers including the default writer by simply setting the mode in the configuration step as in the example below.
```swift
TraceLog.configure(mode: .direct)
```
This will set TraceLog's default writer to `.direct` mode.

You can also configure each writer individually by wrapping the writer in a mode as in the example below.

```swift
TraceLog.configure(writers: [.direct(ConsoleWriter()), .async(FileWriter())])
```
This will set the ConsoleWriter to write directly (synchronously) when you log but will queue the FileWriter calls to write in the background asynchronously.

You can also set all writers to the same mode by setting the default mode and passing the writer as normal as in the following statement.

```swift
TraceLog.configure(mode: .direct, writers: [ConsoleWriter(), FileWriter()])
```
This sets both the ConsoleWriter and the FileWriter to `.direct` mode.

# Setting up your debug environment

TraceLog's current logging output is controlled by variables that are either set in the runtime environment or passed on startup of the application (in the `TraceLog.Configure(environment:)` method).

These variables consist of the following:

```shell
LOG_ALL=<LEVEL>
LOG_TAG_<TAGNAME>=<LEVEL>
LOG_PREFIX_<TAGPREFIX>=<LEVEL>
```
Log output can be set globally using the `LOG_ALL` variable, by TAG name
using the `LOG_TAG_<TAGNAME>` variable pattern, and/or by a TAG prefix by using
the `LOG_PREFIX_<TAGPREFIX>` variable pattern.

Each environment variable set is set with a level as the value. The following levels are available in order of display priority. Each level encompasses the level below it with `TRACE4` including the output from every level. The lowest level setting is `OFF` which turns logging off for the level set.

Levels:
```shell
TRACE4
TRACE3
TRACE2
TRACE1
INFO
WARNING
ERROR
OFF
```
> Note: Log level `OFF` does not disable **TraceLog** completely, it only suppresses log messages for the time it is set in the environment during your debug session. To completely disable **TraceLog** please see [Runtime Overhead](#runtime-overhead) for more information.
Multiple Environment variables can be set at one time to get the desired level of visibility into the workings of the app. Here are some examples.

Suppose you wanted the first level of `TRACE` logging for your Networking module which has a class prefix of NT and you wanted to see only errors and warnings for the rest of the application. You would set the following:

```shell
LOG_ALL=WARNING
LOG_PREFIX_NT=TRACE1
```

More specific settings override less specific so in the above example, the less specific setting is `LOG_ALL` which is set to `WARNING`. The tag prefix is specifying a particular collection of tags that start with the string CS, so this is more specific and overrides
the `LOG_ALL`. If you choose to name a particular tag, that will override the prefix settings.

For instance, in the example above, if we decided for one tag in the Networking module, we needed more output, we could set the following:
```shell
LOG_ALL=WARNING
LOG_PREFIX_NT=TRACE1
LOG_TAG_NTSerializer=TRACE4
```
This outputs the same as the previous example except for the `NTSerializer` tag which is set to `TRACE4` instead of using the less specific `TRACE1` setting in `LOG_PREFIX`.


### Environment Setup (Xcode)

To set up the environment using Xcode, select "Edit Scheme" from the "Set the active scheme" menu at the top left. That brings up the menu below.

<img src=Documentation/Xcode-environment-setup-screenshot.png width=597 height=361 />

### Environment Setup (Statically in code)

TraceLog.configure() accepts an optional parameter called environment which you can pass the environment. This allows you to set the configuration options at startup time (note: this ignores any variables passed in the environment).

Here is an example of setting the configuration via `TraceLog.configure()`.
```swift
TraceLog.configure(environment: ["LOG_ALL": "TRACE4",
"LOG_PREFIX_NS" : "ERROR",
"LOG_TAG_TraceLog" : "TRACE4"])
```

Note: Although the environment is typically set once at the beginning of the application, `TraceLog.configure`
can be called anywhere in your code as often as required to reconfigured the logging levels.
126 changes: 126 additions & 0 deletions Documentation/Logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Logging

## Swift

For Swift TraceLog comes with the following basic Logging functions (Note: hidden
parameters and defaults were omitted for simplicity).

```swift
logError (tag: String?, message: @escaping () -> String)
logWarning(tag: String?, message: @escaping () -> String)
logInfo (tag: String?, message: @escaping () -> String)
logTrace (tag: String?, level: UInt, message: @escaping () -> String)
logTrace (level: UInt, @escaping message: () -> String)
```

Using it is as simple as calling one of the methods depending on the current type of message you want to log, for instance, to log a simple informational message.

```swift
logInfo { "A simple informational message" }
```

Since the message parameter is a closure that evaluates to a String, any expression that results in a string message can be used.

```swift
logInfo {
"A simple informational message: " +
" Another expression or constant that evaluates to a string"
}
```

We used closures for several reasons; one is that the closure will not be evaluated (and you won't incur the overhead) if logging is disabled or if the log level for this call is higher than the current log level set. And two, more complex expressions can be put into the closure to make decisions on the message to be printed based on the current context of the call. Again, these complex closures will not get executed in the cases mentioned above. For instance:

```swift
logInfo {
if let unwrappedOptionalString = optionalString {
return "Executing with \(unwrappedOptionalString)..."
} else {
return "Executing..."
}
}
```

Log methods take an optional tag that you can use to group related messages and also be used to determine whether this statement gets logged based on the current environment configuration. It no tag is set, the file name of the calling method is used as the tag.

```swift
logInfo("MyCustomTag") {
"A simple informational message"
}
```

There are several trace levels for `logTrace` that can be used. If you don't pass a level, you get level 3, otherwise, specify a level when making the `logTrace` call. For example, here is a trace level 1 and a level 3 call.

```swift
logTrace {
"A simple trace level 1 message"
}

logTrace(3) {
"A simple trace level 3 message"
}
```

You can of course also pass a tag like the rest of the log calls.

```swift
logTrace("MyCustomTag", level: 3) {
"A simple trace level message"
}
```

That is all there is to adding logging to your **Swift** application!

## Objective-C

As with Swift using TraceLog with Objective-C is extremely simple out of the box. The Objective-C implementation consists of the following primary logging methods. Each has a format specifier (like `NSLog`) and an optional variable number of arguments that represent your placeholder replacement values.

```objc
LogError (format,...)
LogWarning(format,...)
LogInfo (format,...)
LogTrace (level,format,...)
```
Using it is as simple as calling one of the methods depending on the current type of message you want to log, for instance, to log a simple informational message.
```objc
LogInfo(@"A simple informational message");
```

You can also call it as you would `NSlog` by using the format specifier and parameters for placeholder replacement.

```objc
LogInfo(@"A simple informational message: %@", @"Another NSString or expression that evaluates to an NSString");
```
More complex expressions can be put into the placeholder values by using Objective-C blocks that return a printable NSObject. These can be used to make decisions on the message to be printed based on the current context of the call. These complex blocks will not get executed (and you won't incur the overhead) if logging is disabled or if the log level for this call is higher than the current log level set. For instance:
```objc
LogInfo(@"Executing%@...",
^() {
if (optionalString != nil) {
return [NSString stringWithFormat: @" with %@", optionalString];
} else {
return @"";
}
}()
);
```

There is a special version of Log methods take an optional tag that you can use to group related messages and also be used to determine whether this statement gets logged based on the current environment configuration. These methods begin with C (e.g. `CLogInfo`).

```objc
CLogInfo(@"MyCustomTag", @"A simple informational message");
```
There are several trace levels for `LogTrace` that can be used. For example, here is a trace level 3 call.
```objc
LogTrace(3, @"A simple trace level message");
```

You can of course also pass a tag by using the CLog version of the call.

```objc
CLogTrace(3, @"MyCustomTag", @"A simple trace level message");
```
63 changes: 63 additions & 0 deletions Documentation/Quick Start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Quick Start Guide

Using TraceLog is incredibly simple out of the box. Although TraceLog is highly configurable, to get started all you have to do is add the pod to your project,
import TraceLog to the files that require logging and start adding log statements where you need them. TraceLog initializes itself and does everything else for you.

## Add TraceLog to your project

In your `Podfile` add TraceLog.

```ruby
target 'MyApp'

pod "TraceLog", '~>5.0'
```
If you have mixed Swift and Objective-C code, you must specify the subspec to enable Objective-C as follows:

```ruby
target 'MyApp'

pod "TraceLog", '~>5.0'
pod "TraceLog/ObjC", '~>5.0'
```

## Import TraceLog and Start logging

Import TraceLog into you files and start logging.

```swift
import TraceLog

struct MyClass {

func doSomething() {

LogInfo { "A simple TraceLog Test message" }
}
}
```

## Log Functions

TraceLog has the following primary logging functions to log various levels of information. The output of these can be controlled via the environment variables at runtime or programmatically at application startup via the `TraceLog.configure()` func.

```swift
logError (tag: String?, message: @escaping () -> String)
logWarning(tag: String?, message: @escaping () -> String)
logInfo (tag: String?, message: @escaping () -> String)
logTrace (tag: String?, level: UInt, message: @escaping () -> String)
logTrace (level: UInt, @escaping message: () -> String)
```
> Note: hidden parameters and defaults were omitted for simplicity.
## Basic Configuration

Although not strictly require, calling the `TraceLog.configure()` command at startup will allow TraceLog to read the environment for configuration information.

Simply call configure with no parameters as early as possible in your startup code (preferably before ay log statements get called.)

```swift
TraceLog.configure()
```

For more advanced configuration please see [TraceLog Configuration](Configuration.md)
Loading

0 comments on commit 1ad3b00

Please sign in to comment.