diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj
index c3b2fad..7355162 100644
--- a/Logger.xcodeproj/project.pbxproj
+++ b/Logger.xcodeproj/project.pbxproj
@@ -12,6 +12,10 @@
 		2EBF4B4A2122AF53008E4117 /* AgregateLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B472122AF53008E4117 /* AgregateLogger.swift */; };
 		2EBF4B4B2122AF53008E4117 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B482122AF53008E4117 /* ConsoleLogger.swift */; };
 		2EBF4B4C2122AF53008E4117 /* NullLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B492122AF53008E4117 /* NullLogger.swift */; };
+		2EBF4B512122B06E008E4117 /* NSFileHandle+Swift.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B502122B06E008E4117 /* NSFileHandle+Swift.m */; };
+		2EBF4B572122B598008E4117 /* DiskLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B542122B598008E4117 /* DiskLogger.swift */; };
+		2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B552122B598008E4117 /* FileWriter.swift */; };
+		2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -33,6 +37,12 @@
 		2EBF4B472122AF53008E4117 /* AgregateLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgregateLogger.swift; sourceTree = "<group>"; };
 		2EBF4B482122AF53008E4117 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = "<group>"; };
 		2EBF4B492122AF53008E4117 /* NullLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NullLogger.swift; sourceTree = "<group>"; };
+		2EBF4B4F2122B06E008E4117 /* NSFileHandle+Swift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSFileHandle+Swift.h"; sourceTree = "<group>"; };
+		2EBF4B502122B06E008E4117 /* NSFileHandle+Swift.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+Swift.m"; sourceTree = "<group>"; };
+		2EBF4B532122B2AA008E4117 /* Logger-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Logger-Bridging-Header.h"; sourceTree = "<group>"; };
+		2EBF4B542122B598008E4117 /* DiskLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskLogger.swift; sourceTree = "<group>"; };
+		2EBF4B552122B598008E4117 /* FileWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileWriter.swift; sourceTree = "<group>"; };
+		2EBF4B562122B598008E4117 /* Logrotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logrotate.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -65,6 +75,7 @@
 		2EBF4B3C2122AA34008E4117 /* Logger */ = {
 			isa = PBXGroup;
 			children = (
+				2EBF4B532122B2AA008E4117 /* Logger-Bridging-Header.h */,
 				2EBF4B442122ACD6008E4117 /* LogStringConvertible.swift */,
 				2EBF4B3D2122AA34008E4117 /* Logger.swift */,
 				2EBF4B462122AF53008E4117 /* Loggers */,
@@ -78,10 +89,23 @@
 				2EBF4B472122AF53008E4117 /* AgregateLogger.swift */,
 				2EBF4B482122AF53008E4117 /* ConsoleLogger.swift */,
 				2EBF4B492122AF53008E4117 /* NullLogger.swift */,
+				2EBF4B4D2122B034008E4117 /* DiskLogger */,
 			);
 			path = Loggers;
 			sourceTree = "<group>";
 		};
+		2EBF4B4D2122B034008E4117 /* DiskLogger */ = {
+			isa = PBXGroup;
+			children = (
+				2EBF4B542122B598008E4117 /* DiskLogger.swift */,
+				2EBF4B552122B598008E4117 /* FileWriter.swift */,
+				2EBF4B562122B598008E4117 /* Logrotate.swift */,
+				2EBF4B4F2122B06E008E4117 /* NSFileHandle+Swift.h */,
+				2EBF4B502122B06E008E4117 /* NSFileHandle+Swift.m */,
+			);
+			path = DiskLogger;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -114,6 +138,7 @@
 				TargetAttributes = {
 					2EBF4B392122AA34008E4117 = {
 						CreatedOnToolsVersion = 10.0;
+						LastSwiftMigration = 1000;
 					};
 				};
 			};
@@ -139,10 +164,14 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */,
+				2EBF4B572122B598008E4117 /* DiskLogger.swift in Sources */,
 				2EBF4B4C2122AF53008E4117 /* NullLogger.swift in Sources */,
 				2EBF4B3E2122AA34008E4117 /* Logger.swift in Sources */,
+				2EBF4B512122B06E008E4117 /* NSFileHandle+Swift.m in Sources */,
 				2EBF4B4B2122AF53008E4117 /* ConsoleLogger.swift in Sources */,
 				2EBF4B4A2122AF53008E4117 /* AgregateLogger.swift in Sources */,
+				2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */,
 				2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -269,10 +298,18 @@
 		2EBF4B422122AA34008E4117 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "Logger/Logger-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -281,10 +318,17 @@
 		2EBF4B432122AA34008E4117 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "Logger/Logger-Bridging-Header.h";
 				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
diff --git a/Logger/Logger-Bridging-Header.h b/Logger/Logger-Bridging-Header.h
new file mode 100644
index 0000000..6a5bec0
--- /dev/null
+++ b/Logger/Logger-Bridging-Header.h
@@ -0,0 +1,5 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import "Loggers/DiskLogger/NSFileHandle+Swift.h"
diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift
new file mode 100644
index 0000000..49e6441
--- /dev/null
+++ b/Logger/Loggers/DiskLogger/DiskLogger.swift
@@ -0,0 +1,91 @@
+//
+// MIT License
+//
+// Copyright (c) 2018 Wojciech Nagrodzki
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+import Foundation
+
+public final class DiskLogger: Logger {
+    private let fileURL: URL
+    private let fileSizeLimit: UInt64
+    private let rotations: Int
+    private let queue = DispatchQueue(label: "com.wnagrodzki.DiskLogger", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil)
+    private var buffer = Data()
+    private var fileWriter: FileWriter!
+    
+    public init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int) {
+        self.fileURL = fileURL
+        self.fileSizeLimit = fileSizeLimit
+        self.rotations = rotations
+    }
+    
+    public func log(time: String, level: LogLevel, location: String, object: String) {
+        let message = time + " <" + level.rawValue + "> " + location + " " + object + "\n"
+        log(message)
+    }
+    
+    private func log(_ message: String) {
+        guard let data = message.data(using: .utf8) else {
+            log("Message failed to convert to UTF8")
+            return
+        }
+        queue.async {
+            self.buffer.append(data)
+            
+            do {
+                try self.openFileWriter()
+                try self.writeBuffer()
+            }
+            catch is FileWriter.FileSizeLimitReached {
+                self.closeFileWriter()
+                try? self.rotateLogFiles()
+            }
+            catch {
+                let message = String(describing: error)
+                self.log(message)
+            }
+        }
+    }
+    
+    private func openFileWriter() throws {
+        guard fileWriter == nil else { return }
+        if FileManager.default.fileExists(atPath: fileURL.path) == false {
+            FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: nil)
+        }
+        fileWriter = try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit)
+    }
+    
+    private func writeBuffer() throws {
+        try fileWriter.write(buffer)
+        buffer.removeAll()
+    }
+    
+    private func closeFileWriter() {
+        self.fileWriter.synchronizeAndCloseFile()
+        self.fileWriter = nil
+    }
+    
+    private func rotateLogFiles() throws {
+        let logrotate = Logrotate(fileURL: fileURL, rotations: rotations)
+        try logrotate.rotate()
+    }
+}
diff --git a/Logger/Loggers/DiskLogger/FileWriter.swift b/Logger/Loggers/DiskLogger/FileWriter.swift
new file mode 100644
index 0000000..0a624f1
--- /dev/null
+++ b/Logger/Loggers/DiskLogger/FileWriter.swift
@@ -0,0 +1,54 @@
+//
+// MIT License
+//
+// Copyright (c) 2018 Wojciech Nagrodzki
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+import Foundation
+
+final class FileWriter {
+    
+    struct FileSizeLimitReached: Error {}
+    
+    private let handle: FileHandle
+    private let sizeLimit: UInt64
+    private var currentSize: UInt64
+    
+    init(fileURL: URL, fileSizeLimit: UInt64) throws {
+        handle = try FileHandle(forWritingTo: fileURL)
+        self.sizeLimit = fileSizeLimit
+        currentSize = handle.seekToEndOfFile()
+    }
+    
+    func write(_ data: Data) throws {
+        let dataSize = UInt64(data.count)
+        guard currentSize + dataSize <= sizeLimit else {
+            throw FileSizeLimitReached()
+        }
+        try handle.swift_write(data)
+        currentSize += dataSize
+    }
+    
+    func synchronizeAndCloseFile() {
+        handle.synchronizeFile()
+        handle.closeFile()
+    }
+}
diff --git a/Logger/Loggers/DiskLogger/Logrotate.swift b/Logger/Loggers/DiskLogger/Logrotate.swift
new file mode 100644
index 0000000..4d177e5
--- /dev/null
+++ b/Logger/Loggers/DiskLogger/Logrotate.swift
@@ -0,0 +1,54 @@
+//
+// MIT License
+//
+// Copyright (c) 2018 Wojciech Nagrodzki
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+import Foundation
+
+final class Logrotate {
+    private let fileURL: URL
+    private let rotations: Int
+    
+    init(fileURL: URL, rotations: Int) {
+        precondition(rotations > 0)
+        self.fileURL = fileURL
+        self.rotations = rotations
+    }
+    
+    func rotate() throws {
+        let range = 1...rotations
+        let pathExtensions = range.map { "\($0)" }
+        let rotatedURLs = pathExtensions.map { fileURL.appendingPathExtension($0) }
+        let allURLs = [fileURL] + rotatedURLs
+        
+        let toDelete = rotatedURLs.last!
+        let toMove = zip(allURLs, rotatedURLs).reversed()
+        
+        if FileManager.default.fileExists(atPath: toDelete.path) {
+            try FileManager.default.removeItem(at: toDelete)
+        }
+        for (oldURL, newURL) in toMove {
+            guard FileManager.default.fileExists(atPath: oldURL.path) else { continue }
+            try FileManager.default.moveItem(at: oldURL, to: newURL)
+        }
+    }
+}
diff --git a/Logger/Loggers/DiskLogger/NSFileHandle+Swift.h b/Logger/Loggers/DiskLogger/NSFileHandle+Swift.h
new file mode 100644
index 0000000..23f3d4e
--- /dev/null
+++ b/Logger/Loggers/DiskLogger/NSFileHandle+Swift.h
@@ -0,0 +1,35 @@
+//
+// MIT License
+//
+// Copyright (c) 2018 Wojciech Nagrodzki
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface NSFileHandle (Swift)
+
+- (BOOL)swift_writeData:(NSData *)data error:(NSError **)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Logger/Loggers/DiskLogger/NSFileHandle+Swift.m b/Logger/Loggers/DiskLogger/NSFileHandle+Swift.m
new file mode 100644
index 0000000..994e667
--- /dev/null
+++ b/Logger/Loggers/DiskLogger/NSFileHandle+Swift.m
@@ -0,0 +1,44 @@
+//
+// MIT License
+//
+// Copyright (c) 2018 Wojciech Nagrodzki
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+#import "NSFileHandle+Swift.h"
+
+@implementation NSFileHandle (Swift)
+
+- (BOOL)swift_writeData:(NSData *)data error:(NSError **)error {
+    @try {
+        [self writeData:data];
+        return YES;
+    }
+    @catch (NSException *exception) {
+        if (error == nil) {
+            return NO;
+        }
+        NSDictionary * userInfo = @{NSLocalizedFailureReasonErrorKey: exception.reason};
+        *error = [NSError errorWithDomain:@"NSFileHandleException" code:1 userInfo:userInfo];
+        return NO;
+    }
+}
+
+@end