
XCGlogger es el módulo de registro de depuración original para su uso en proyectos Swift.
Swift no incluye un preprocesador C, por lo que los desarrolladores no pueden usar el registro de depuración #define MacRos que usarían en Objective-C. Esto significa que nuestra forma tradicional de generar buenos registros de depuración ya no funciona. Recurrir a llamadas print antiguas simples significa que pierde mucha información útil, o requiere que escriba mucho más código.
XCGlogger le permite registrar los detalles en la consola (y opcionalmente un archivo u otros destinos personalizados), al igual que lo habría hecho con NSLog() o print() , pero con información adicional, como la fecha, el nombre de la función, el nombre de archivo y el número de línea.
Ve de esto:
Simple message
A esto:
2014-06-09 06:44:43.600 [Debug] [AppDelegate.swift:40] application(_:didFinishLaunchingWithOptions:): Simple message
Ejecutar:
git submodule add https://github.com/DaveWoodCom/XCGLogger.git
en su carpeta de repositorio.
Agregue la siguiente línea a su Cartfile .
github "DaveWoodCom/XCGLogger" ~> 7.1.5
Luego ejecute carthage update --no-use-binaries o simplemente carthage update . Para obtener detalles sobre la instalación y el uso de Carthage, visite su página del proyecto.
Los desarrolladores que ejecutan 5.0 y arriba en Swift deberán agregar $(SRCROOT)/Carthage/Build/iOS/ObjcExceptionBridging.framework a sus archivos de entrada en la fase de compilación de los marcos de carpas de Carthage.
Agregue algo similar a las siguientes líneas a su Podfile . Es posible que deba ajustar en función de su plataforma, versión/rama, etc.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '12.0'
use_frameworks!
pod 'XCGLogger', '~> 7.1.5'
Especificar el Pod XCGLogger por sí solo incluirá el marco principal. Estamos comenzando a agregar subespecs para permitirle incluir componentes opcionales también:
pod 'XCGLogger/UserInfoHelpers', '~> 7.1.5' : incluya algún código experimental para ayudar a tratar el uso de los diccionarios UserInfo para etiquetar los mensajes de registro.
Luego ejecute pod install . Para obtener detalles sobre la instalación y el uso de Cocoapods, visite su sitio web oficial.
Nota: Antes de Cocoapods 1.4.0 no era posible usar múltiples vainas con una mezcla de versiones rápidas. Es posible que deba asegurarse de que cada POD esté configurado para la versión Swift correcta (verifique los objetivos en el proyecto POD de su espacio de trabajo). Si ajusta manualmente la versión Swift para un proyecto, restablecerá la próxima vez que ejecute pod install . Puede agregar un gancho post_install a su Podfile para automatizar la configuración de las versiones Swift correctas. Esto no se ha probado en gran medida, y no estoy seguro de que sea una buena solución, pero parece funcionar:
post_install do |installer|
installer.pods_project.targets.each do |target|
if ['SomeTarget-iOS', 'SomeTarget-watchOS'].include? "#{target}"
print "Setting #{target}'s SWIFT_VERSION to 4.2n"
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.2'
end
else
print "Setting #{target}'s SWIFT_VERSION to Undefined (Xcode will automatically resolve)n"
target.build_configurations.each do |config|
config.build_settings.delete('SWIFT_VERSION')
end
end
end
print "Setting the default SWIFT_VERSION to 3.2n"
installer.pods_project.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.2'
end
end
Puede ajustar eso para satisfacer sus necesidades, por supuesto.
Agregue la siguiente entrada a las dependencias de su paquete:
.Package(url: "https://github.com/DaveWoodCom/XCGLogger.git", majorVersion: 7)
Usar:
Este método de inicio rápido está destinado solo a ponerlo en funcionamiento con el registrador. Sin embargo, debe usar el uso avanzado a continuación para aprovechar al máximo esta biblioteca.
Agregue el proyecto XCGlogger como subproyecto a su proyecto y agregue la biblioteca apropiada como una dependencia de sus objetivos. En la pestaña General de su objetivo, agregue XCGLogger.framework y ObjcExceptionBridging.framework a la sección Embedded Binaries .
Luego, en cada archivo de origen:
import XCGLoggerEn su AppDelegate (u otro archivo global), declare una constante global a la instancia de XCGlogger predeterminada.
let log = XCGLogger . defaultEn el
application ( _ application : UIApplication , didFinishLaunchingWithOptions launchOptions : [ UIApplicationLaunchOptionsKey : Any ] ? = nil ) // iOS, tvOSo
applicationDidFinishLaunching ( _ notification : Notification ) // macOSfunción, configure las opciones que necesita:
log . setup ( level : . debug , showThreadName : true , showLevel : true , showFileNames : true , showLineNumbers : true , writeToFile : " path/to/file " , fileLevel : . debug ) El valor para writeToFile: puede ser una String o URL . Si el archivo ya existe, se borrará antes de usarlo. Omita el parámetro o configúrelo en nil para registrar solo a la consola. Opcionalmente, puede establecer un nivel de registro diferente para la salida del archivo utilizando el parámetro fileLevel: :. Contácelo en nil u omitirlo para usar el mismo nivel de registro que la consola.
Luego, siempre que desee registrar algo, use uno de los métodos de conveniencia:
log . verbose ( " A verbose message, usually useful when working on a specific problem " )
log . debug ( " A debug message " )
log . info ( " An info message, probably useful to power users looking in console.app " )
log . notice ( " A notice message " )
log . warning ( " A warning message, may indicate a possible error " )
log . error ( " An error occurred, but it's recoverable, just info about what happened " )
log . severe ( " A severe error occurred, we are likely about to crash now " )
log . alert ( " An alert error occurred, a log destination could be made to email someone " )
log . emergency ( " An emergency error occurred, a log destination could be made to text someone " ) Los diferentes métodos establecen el nivel de registro del mensaje. XCGlogger solo imprimirá mensajes con un nivel de registro que es mayor o igual a su configuración de nivel de registro actual. Entonces, un registrador con un nivel de .error solo generará mensajes de registro con un nivel de .error , .severe , .alert o .emergency .
XCGlogger tiene como objetivo ser simple de usar y hacer que se ejecute rápidamente con tan solo 2 líneas de código anteriores. Pero permite un control y flexibilidad mucho mayor.
Se puede configurar un registrador para entregar mensajes de registro a una variedad de destinos. Usando la configuración básica anterior, el registrador emitirá mensajes de registro a la consola de depuración XCODE estándar, y opcionalmente un archivo si se proporciona una ruta. Es muy probable que quiera enviar registros a lugares más interesantes, como la consola del sistema Apple, una base de datos, un servidor de terceros u otra aplicación como NSLogger. Esto se logra agregando el destino al registrador.
Aquí hay un ejemplo de configuración del registrador para emitir el registro del sistema Apple, así como un archivo.
// Create a logger object with no destinations
let log = XCGLogger ( identifier : " advancedLogger " , includeDefaultDestinations : false )
// Create a destination for the system console log (via NSLog)
let systemDestination = AppleSystemLogDestination ( identifier : " advancedLogger.systemDestination " )
// Optionally set some configuration options
systemDestination . outputLevel = . debug
systemDestination . showLogIdentifier = false
systemDestination . showFunctionName = true
systemDestination . showThreadName = true
systemDestination . showLevel = true
systemDestination . showFileName = true
systemDestination . showLineNumber = true
systemDestination . showDate = true
// Add the destination to the logger
log . add ( destination : systemDestination )
// Create a file log destination
let fileDestination = FileDestination ( writeToFile : " /path/to/file " , identifier : " advancedLogger.fileDestination " )
// Optionally set some configuration options
fileDestination . outputLevel = . debug
fileDestination . showLogIdentifier = false
fileDestination . showFunctionName = true
fileDestination . showThreadName = true
fileDestination . showLevel = true
fileDestination . showFileName = true
fileDestination . showLineNumber = true
fileDestination . showDate = true
// Process this destination in the background
fileDestination . logQueue = XCGLogger . logQueue
// Add the destination to the logger
log . add ( destination : fileDestination )
// Add basic app info, version info etc, to the start of the logs
log . logAppDetails ( )Puede configurar cada destino de registro con diferentes opciones según sus necesidades.
Otro patrón de uso común es tener múltiples registradores, quizás uno para problemas de IU, uno para redes y otro para problemas de datos.
Cada destino de registro puede tener su propio nivel de registro. Como conveniencia, puede establecer el nivel de registro en el objeto de registro en sí y pasará ese nivel a cada destino. Luego establezca los destinos que necesitan ser diferentes.
Nota : Un objeto de destino solo se puede agregar a un objeto de registrador, lo que lo agregará a un segundo lo eliminará del primero.
Alternativamente, puede usar un cierre para inicializar su variable global, de modo que toda la inicialización se realice en un solo lugar
let log : XCGLogger = {
let log = XCGLogger ( identifier : " advancedLogger " , includeDefaultDestinations : false )
// Customize as needed
return log
} ( ) Nota : Esto crea el objeto de registro perezosamente, lo que significa que no se crea hasta que realmente sea necesario. Esto retrasa la salida inicial de los detalles de la información de la aplicación. Debido a esto, recomiendo forzar el objeto de registro que se creará en el lanzamiento de la aplicación agregando la línea let _ = log en la parte superior de su método didFinishLaunching si aún no registra algo en el lanzamiento de la aplicación.
Puedes registrar cadenas:
log . debug ( " Hi there! " )o casi cualquier cosa que quieras:
log . debug ( true )
log . debug ( CGPoint ( x : 1.1 , y : 2.2 ) )
log . debug ( MyEnum . Option )
log . debug ( ( 4 , 2 ) )
log . debug ( [ " Device " : " iPhone " , " Version " : 7 ] ) Nuevo en XCGlogger 4, ahora puede crear filtros para aplicar a su registrador (o a destinos específicos). Cree y configure sus filtros (ejemplos a continuación), y luego agréguelos al registrador o objetos de destino estableciendo la propiedad de filters opcionales en una matriz que contiene los filtros. Los filtros se aplican en el orden que existen en la matriz. Durante el procesamiento, se pregunta a cada filtro si el mensaje de registro debe excluirse del registro. Si algún filtro excluye el mensaje de registro, está excluido. Los filtros no tienen forma de revertir la exclusión de otro filtro.
Si la propiedad filters de un destino es nil , la propiedad filters del registro se utiliza en su lugar. Para que un destino registre todo, mientras que todos los demás destinos filtran algo, agregue los filtros al objeto de registro y establezca la propiedad filters de un destino en una matriz vacía [] .
Nota : A diferencia de los destinos, puede agregar el mismo objeto de filtro a múltiples registradores y/o múltiples destinos.
Para excluir todos los mensajes de registro de un archivo específico, cree un filtro de exclusión como así:
log . filters = [ FileNameFilter ( excludeFrom : [ " AppDelegate.swift " ] , excludePathWhenMatching : true ) ] excludeFrom: toma una Array<String> o Set<String> para que pueda especificar varios archivos al mismo tiempo.
excludePathWhenMatching: predeterminado es true para que pueda omitirlo a menos que desee hacer coincidir también.
Para incluir mensajes de registro solo para un conjunto específico en archivos, cree el filtro usando el includeFrom: inicializador. También es posible alternar la propiedad inverse para voltear el filtro de exclusión a un filtro de inclusión.
Para filtrar los mensajes de registro por etiqueta, por supuesto, debe poder establecer una etiqueta en los mensajes de registro. Cada mensaje de registro ahora puede tener datos adicionales definidos por el usuario adjuntos a ellos, para ser utilizados por filtros (y/o formatters, etc.). Esto se maneja con un userInfo: Dictionary<String, Any> . La clave del diccionario debe ser una cadena con nombación para evitar colisiones con adiciones futuras. Las teclas oficiales comenzarán con com.cerebralgardens.xcglogger . La tecla TAG puede acceder por XCGLogger.Constants.userInfoKeyTags . Definitivamente no desea escribir eso, así que siéntase libre de crear un atajo global: let tags = XCGLogger.Constants.userInfoKeyTags . Ahora puede etiquetar fácilmente sus registros:
let sensitiveTag = " Sensitive "
log . debug ( " A tagged log message " , userInfo : [ tags : sensitiveTag ] ) El valor para las etiquetas puede ser una Array<String> , Set<String> o simplemente una String , dependiendo de sus necesidades. Todos funcionarán de la misma manera cuando se filtren.
Dependiendo de su flujo de trabajo y uso, probablemente creará métodos más rápidos para configurar el diccionario userInfo . Vea a continuación para obtener otros atajos posibles.
Ahora que tiene sus registros etiquetados, puede filtrar fácilmente:
log . filters = [ TagFilter ( excludeFrom : [ sensitiveTag ] ) ] Al igual que FileNameFilter , puede usar includeFrom: o alternar inverse para incluir solo mensajes de registro que tienen las etiquetas especificadas.
El filtrado por desarrollador es exactamente como filtrar por etiqueta, solo utilizando la tecla userInfo de XCGLogger.Constants.userInfoKeyDevs . De hecho, ambos filtros son subclases de la clase UserInfoFilter que puede usar para crear filtros adicionales. Vea la extensión de XCGlogger a continuación.
En proyectos grandes con múltiples desarrolladores, probablemente desee comenzar a etiquetar mensajes de registro e indicar el desarrollador que agregó el mensaje.
Si bien es extremadamente flexible, el diccionario userInfo puede ser un poco engorroso de usar. Hay algunos métodos posibles que puede usar para simplemente cosas. Todavía estoy probando estos yo mismo para que todavía no formen parte de la biblioteca (me encantan los comentarios u otras sugerencias).
He creado algún código experimental para ayudar a crear los diccionarios UserInfo. (Incluya el subespec UserInfoHelpers opcional si usa cocoapods). Consulte la aplicación de demostración de iOS para verla en uso.
Hay dos estructuras que se ajustan al protocolo UserInfoTaggingProtocol . Tag y Dev .
Puede crear una extensión en cada uno de estos que se adapte a su proyecto. Por ejemplo:
extension Tag {
static let sensitive = Tag ( " sensitive " )
static let ui = Tag ( " ui " )
static let data = Tag ( " data " )
}
extension Dev {
static let dave = Dev ( " dave " )
static let sabby = Dev ( " sabby " )
} Junto con estos tipos, hay un operador sobrecargado | Eso se puede utilizar para fusionarlos en un diccionario compatible con UserInfo: Parámetro de las llamadas de registro.
Entonces puede registrar mensajes como este:
log . debug ( " A tagged log message " , userInfo : Dev . dave | Tag . sensitive ) Hay algunos problemas actuales que veo con estos UserInfoHelpers , por lo que lo he hecho opcional/experimental por ahora. Me encantaría escuchar comentarios/sugerencias de mejoras.
| Fusiona diccionarios siempre que no haya Set s. Si uno de los diccionarios contiene un Set , usará uno de ellos, sin fusionarlos. Preferir el lado izquierdo si ambos lados tienen un conjunto para la misma tecla.userInfo: necesita un diccionario, no puede pasar en un solo objeto de desarrollo o etiqueta. Necesitas usar al menos dos con el | Operador para que se convierta automáticamente en un diccionario compatible. Si solo desea una etiqueta, por ejemplo, debe acceder al parámetro .dictionary manualmente: userInfo: Tag("Blah").dictionary . Todos los métodos de registro funcionan con cierres. Utilizando el mismo azúcar sintáctico que la función assert() de Swift, este enfoque asegura que no desperdiciemos recursos construyendo mensajes de registro que no se emitan de todos modos, al mismo tiempo que preservan un sitio de llamadas limpias.
Por ejemplo, la siguiente declaración de registro no desperdiciará recursos si se suprime el nivel de registro de depuración:
log . debug ( " The description of ( thisObject ) is really expensive to create " ) Del mismo modo, supongamos que tiene que iterar a través de un bucle para hacer algún cálculo antes de registrar el resultado. En Objective-C, puede colocar ese bloque de código entre #if #endif , y evitar que el código se ejecute. Pero en Swift, anteriormente necesitaría procesar ese bucle, desperdiciando recursos. Con XCGLogger es tan simple como:
log . debug {
var total = 0.0
for receipt in receipts {
total += receipt . total
}
return " Total of all receipts: ( total ) "
} En los casos en que desea ejecutar selectivamente el código sin generar una línea de registro, devolver nil o usar uno de los métodos: verboseExec , debugExec , infoExec , warningExec , errorExec y severeExec .
Puede crear su propio objeto DateFormatter y asignarlo al Logger.
let dateFormatter = DateFormatter ( )
dateFormatter . dateFormat = " MM/dd/yyyy hh:mma "
dateFormatter . locale = Locale . current
log . dateFormatter = dateFormatterXCGlogger admite agregar códigos de formato a sus mensajes de registro para habilitar el color en varios lugares. La opción original era usar el complemento XCodeColors. Sin embargo, Xcode (a partir de la versión 8) ya no admite oficialmente complementos. Todavía puede ver sus registros en color, pero no en Xcode en este momento. Puede usar el soporte de color ANSI para agregar color a sus objetos de Tedestination y ver sus registros a través de una ventana de terminal. ¡Esto le brinda algunas opciones adicionales, como agregar en negrita, cursiva o (por favor no) parpadear!
Una vez habilitado, cada nivel de registro puede tener su propio color. Estos colores se pueden personalizar como se desea. Si usa múltiples registros, puede configurar alternativamente cada registrador en su propio color.
Un ejemplo de configuración del formateador ANSI:
if let fileDestination : FileDestination = log . destination ( withIdentifier : XCGLogger . Constants . fileDestinationIdentifier ) as? FileDestination {
let ansiColorLogFormatter : ANSIColorLogFormatter = ANSIColorLogFormatter ( )
ansiColorLogFormatter . colorize ( level : . verbose , with : . colorIndex ( number : 244 ) , options : [ . faint ] )
ansiColorLogFormatter . colorize ( level : . debug , with : . black )
ansiColorLogFormatter . colorize ( level : . info , with : . blue , options : [ . underline ] )
ansiColorLogFormatter . colorize ( level : . notice , with : . green , options : [ . italic ] )
ansiColorLogFormatter . colorize ( level : . warning , with : . red , options : [ . faint ] )
ansiColorLogFormatter . colorize ( level : . error , with : . red , options : [ . bold ] )
ansiColorLogFormatter . colorize ( level : . severe , with : . white , on : . red )
ansiColorLogFormatter . colorize ( level : . alert , with : . white , on : . red , options : [ . bold ] )
ansiColorLogFormatter . colorize ( level : . emergency , with : . white , on : . red , options : [ . bold , . blink ] )
fileDestination . formatters = [ ansiColorLogFormatter ]
} Al igual que con los filtros, puede usar los mismos objetos de formato para múltiples registradores y/o múltiples destinos. Si la propiedad formatters de un destino es nil , la propiedad formatters del registrador se utilizará en su lugar.
Consulte la extensión de XCGlogger a continuación para obtener información sobre la creación de sus propios formatúas personalizadas.
Mediante el uso de banderas de compilación Swift, se pueden usar diferentes niveles de registro en la depuración versus la estadificación/producción. Vaya a Configuración de compilación -> Swift Compiler -Fanders personalizados -> Otras banderas Swift y agregue -DDEBUG a la entrada de depuración.
#if DEBUG
log . setup ( level : . debug , showThreadName : true , showLevel : true , showFileNames : true , showLineNumbers : true )
#else
log . setup ( level : . severe , showThreadName : true , showLevel : true , showFileNames : true , showLineNumbers : true )
#endif Puede establecer cualquier cantidad de opciones de manera similar. Consulte la aplicación IOSDemo actualizada para obtener un ejemplo de uso de diferentes destinos de registro basados en opciones, busque USE_NSLOG .
De manera predeterminada, los destinos de registro suministrados procesarán los registros en el hilo que se llaman. Esto es para garantizar que el mensaje de registro se muestre inmediatamente al depurar una aplicación. Puede agregar un punto de interrupción inmediatamente después de una llamada de registro y ver los resultados cuando llegue el punto de interrupción.
Sin embargo, si no está depurando activamente la aplicación, procesar los registros en el hilo actual puede introducir un éxito de rendimiento. Ahora puede especificar un proceso de destino, sus registros en una cola de envío de su elección (o incluso usar una suministrada por defecto).
fileDestination . logQueue = XCGLogger . logQueueo incluso
fileDestination . logQueue = DispatchQueue . global ( qos : . background )Esto funciona extremadamente bien cuando se combina con el método de configuraciones alternativo anterior.
#if DEBUG
log . setup ( level : . debug , showThreadName : true , showLevel : true , showFileNames : true , showLineNumbers : true )
#else
log . setup ( level : . severe , showThreadName : true , showLevel : true , showFileNames : true , showLineNumbers : true )
if let consoleLog = log . logDestination ( XCGLogger . Constants . baseConsoleDestinationIdentifier ) as? ConsoleDestination {
consoleLog . logQueue = XCGLogger . logQueue
}
#endifAl usar la configuración avanzada del registrador (consulte el uso avanzado anterior), ahora puede especificar que el registrador agregue un archivo de registro existente, en lugar de sobrescribirlo automáticamente.
Agregue el parámetro opcional shouldAppend: al inicializar el objeto FileDestination . También puede agregar el parámetro appendMarker: para agregar un marcador al archivo de registro que indica dónde comenzó a agregar una nueva instancia de su aplicación. Por defecto, agregaremos -- ** ** ** -- si se omite el parámetro. Contáctalo en nil para omitir agregar el marcador.
let fileDestination = FileDestination(writeToFile: "/path/to/file", identifier: "advancedLogger.fileDestination", shouldAppend: true, appendMarker: "-- Relauched App --")
Al iniciar sesión en un archivo, tiene la opción de rotar automáticamente el archivo de registro a un destino archivado y hacer que el registrador cree automáticamente un nuevo archivo de registro en lugar del anterior.
Cree un destino utilizando la clase AutoRotatingFileDestination y establezca las siguientes propiedades:
targetMaxFileSize : Rotar automático una vez que el archivo es más grande que este
targetMaxTimeInterval : Rotar automático después de estos segundos
targetMaxLogFiles : número de archivos de registro archivados para mantener, los más antiguos se eliminan automáticamente
Esas son todas las pautas para el registrador, no los límites duros.
Puede crear destinos de registro alternativos (además de los incorporados). Su destino de registro personalizado debe implementar el Protocolo DestinationProtocol . Instanciar su objeto, configurarlo y luego agréguelo al objeto XCGLogger con add(destination:) . Hay dos clases de destino base ( BaseDestination y BaseQueuedDestination ) de las que puede heredar para manejar la mayor parte del proceso para usted, lo que requiere que solo implemente un método adicional en su clase personalizada. Eche un vistazo a ConsoleDestination y FileDestination para ver ejemplos.
También puede crear filtros o formatúas personalizados. Eche un vistazo a las versiones proporcionadas como punto de partida. Tenga en cuenta que los filtros y formateros tienen la capacidad de alterar los mensajes de registro a medida que se procesan. Esto significa que puede crear un filtro que elimine las contraseñas, resalte palabras específicas, cifra mensajes, etc.
XCGlogger es el mejor registrador disponible para Swift debido a las contribuciones de la comunidad como usted. Hay muchas maneras en que puede ayudar a continuar haciéndolo genial.
Nota : Al enviar una solicitud de extracción, utilice muchos pequeños compromisos, una gran compromiso. Hace que sea mucho más fácil fusionarse cuando hay varias solicitudes de extracción que deben combinarse para una nueva versión.
Si encuentra útil esta biblioteca, definitivamente encontrará útil esta otra herramienta:
Watchdog: https://watchdogforxcode.com/
Además, consulte algunos de mis otros proyectos:
El registro de cambios ahora está en su propio archivo: ChangeLog.md