├── LICENSE ├── README.md └── logger.prw /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 NG Informática 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AdvPL Logger 2 | 3 | > Keep track of what happens! Awesome logs for AdvPL 4 | 5 | ![AdvPL Logger](https://i.imgur.com/MQw7hER.png) 6 | 7 | Keeping track of your AdvPL application, either desktop or webservice, may be a complex task. Inserting `ConOut`s everywhere 8 | and exploring the `console.log` file is definitely **not** the way to go, so we created AdvPL Logger! 9 | 10 | AdvPL Logger is a library with support for ANSI VT100 formatted strings that allows you to output your logs to both your 11 | AppServer console and to the file system (with auto resource management). 12 | 13 | ## Examples 14 | 15 | You create a logger passing a **context** to `:New()`, that is, a name to say what is being tracked. 16 | 17 | - Simple 18 | ```xbase 19 | Local oLogger := Logger():New( 'CAPYBARAS_MANAGEMENT' ) 20 | oLogger:Info( 'Capybara server started!' ) 21 | ``` 22 | 23 | - Simulating a REST server 24 | ```xbase 25 | User Function TestLogger 26 | Local nCounter 27 | Local nRandom 28 | Local aMethods 29 | Local aRoutes 30 | Local cMessage 31 | Local oLogger := Logger():New( 'REST_ADVPL' ) 32 | oLogger:Log( 'Starting logger service' ) 33 | oLogger:Info( 'Rest started on port {1}', { 8080 } ) 34 | oLogger:Error( '500 internal server error {1} - {2} {1} {45}', { 'EITA', 'PREULA', 'FOO' } ) 35 | oLogger:Warn( 'GET on /login is deprecated' ) 36 | oLogger:Success( 'Loved all capybaras!' ) 37 | 38 | aMethods := { 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' } 39 | aRoutes := { '/login', '/users', '/user/10', '/capybaras', '/version', '/help' } 40 | For nCounter := 1 To 100 41 | cMessage := { aMethods[ Randomize( 1, 5 ) ], aRoutes[ Randomize( 1, 6 ) ] } 42 | nRandom := Randomize( 1, 15 ) 43 | If nRandom == 6 44 | oLogger:Error( '{1} {2}', cMessage ) 45 | ElseIf nRandom == 5 .Or. nRandom == 9 46 | oLogger:Warn( '{1} {2}', cMessage ) 47 | Else 48 | oLogger:Info( '{1} {2}', cMessage ) 49 | EndIf 50 | Sleep( Randomize( 150, 450 ) ) 51 | Next 52 | Return 53 | ``` 54 | 55 | The log file can be found in your dictionary folder under `logs/`. 56 | 57 | ## Methods 58 | 59 | - `:Log` (gray) 60 | - `:Info` (cyan) 61 | - `:Error` (red) 62 | - `:Warn` (yellow) 63 | - `:Success` (green) 64 | 65 | ## Formatting 66 | 67 | This library supports string formatting with placeholders. You can do such things like: 68 | 69 | ```xbase 70 | oLogger:Success( 'Server started at {1}:{2}', { '127.0.0.1', 8080 } ) 71 | ``` 72 | 73 | All the methods support this format. Keep calm, with support all kind of data inside the formatting array and the 74 | placeholders are positional. 75 | 76 | ## License 77 | 78 | This work is licensed under MIT. All rights reserved. 79 | -------------------------------------------------------------------------------- /logger.prw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'fileio.ch' 3 | 4 | // ANSI/VT-100 sequences for console formatting 5 | #define ANSI_BOLD Chr( 27 ) + '[1m' 6 | #define ANSI_LIGHT_RED Chr( 27 ) + '[91m' 7 | #define ANSI_LIGHT_GREEN Chr( 27 ) + '[92m' 8 | #define ANSI_LIGHT_YELLOW Chr( 27 ) + '[93m' 9 | #define ANSI_CYAN Chr( 27 ) + '[36m' 10 | #define ANSI_LIGHT_GRAY Chr( 27 ) + '[37m' 11 | #define ANSI_LIGHT_MAGENTA Chr( 27 ) + '[95m' 12 | #define ANSI_END Chr( 27 ) + '[0m' 13 | 14 | Static Function Now() 15 | Return ANSI_BOLD + '[' + DToC( Date() ) + ' ' + Time() + ']' + ANSI_END 16 | 17 | Static Function FormatString( cInput, aValues ) 18 | Local nIndex 19 | Local cResult := cInput 20 | 21 | Default aValues := {} 22 | For nIndex := 1 To Len( aValues ) 23 | cResult := StrTran( cResult, '{' + AllTrim( Str( nIndex ) ) + '}', cValToChar( aValues[ nIndex ] ) ) 24 | Next 25 | Return cResult 26 | 27 | /** 28 | * @class Logger 29 | * @author Marcelo Camargo 30 | * @since 02/2018 31 | **/ 32 | Class Logger 33 | Data Context As Character 34 | Data Handler As Number 35 | 36 | Method New() Constructor 37 | Method Start() 38 | Method LogToFile( cKind, cText ) 39 | Method Log( cMessage, aValues ) 40 | Method Info( cMessage, aValues ) 41 | Method Error( cMessage, aValues ) 42 | Method Warn( cMessage, aValues ) 43 | Method Success( cMessage, aValues ) 44 | EndClass 45 | 46 | Method New( cContext ) Class Logger 47 | ::Context := cContext 48 | Return ::Start() 49 | 50 | Method Start() Class Logger 51 | Local cSep := If( isSrvUnix(), '/', '\' ) 52 | Local cOutDir := CurDir() + cSep + 'logs' 53 | Local cOutFile := cOutDir + cSep + ::Context + '.log' 54 | 55 | If !ExistDir( cOutDir ) 56 | MakeDir( cOutDir ) 57 | EndIf 58 | 59 | If File( cOutFile ) 60 | ::Handler := FOpen( cOutFile, FO_WRITE + FO_SHARED, Nil, .F. ) 61 | Else 62 | ::Handler := FCreate( cOutFile, Nil, Nil, .F. ) 63 | EndIf 64 | Return Self 65 | 66 | Method LogToFile( cKind, cText ) Class Logger 67 | Local cLine := DToC( Date() ) + ' ' + Time() + ' | ' 68 | cLine += Padr( '[' + cKind + ']', 11, ' ' ) + ' | ' 69 | cLine += cText + Chr( 13 ) + Chr( 10 ) 70 | FSeek( ::Handler, 0, FS_END ) 71 | FWrite( ::Handler, cLine, Len( cLine ) ) 72 | Return Self 73 | 74 | Method Log( cMessage, aValues ) Class Logger 75 | Local cText := FormatString( cMessage, aValues ) 76 | ConOut( Now() + ' [LOG] ' + ANSI_LIGHT_GRAY + cText + ANSI_END ) 77 | Return ::LogToFile( 'LOG', cText ) 78 | 79 | Method Info( cMessage, aValues ) Class Logger 80 | Local cText := FormatString( cMessage, aValues ) 81 | ConOut( Now() + ' [INFO] ' + ANSI_CYAN + cText + ANSI_END ) 82 | Return ::LogToFile( 'INFO', cText ) 83 | 84 | Method Error( cMessage, aValues ) Class Logger 85 | Local cText := FormatString( cMessage, aValues ) 86 | ConOut( Now() + ' [ERROR] ' + ANSI_LIGHT_RED + cText + ANSI_END ) 87 | Return ::LogToFile( 'ERROR', cText ) 88 | 89 | Method Warn( cMessage, aValues ) Class Logger 90 | Local cText := FormatString( cMessage, aValues ) 91 | ConOut( Now() + ' [WARN] ' + ANSI_LIGHT_YELLOW + cText + ANSI_END ) 92 | Return ::LogToFile( 'WARN', cText ) 93 | 94 | Method Success( cMessage, aValues ) Class Logger 95 | Local cText := FormatString( cMessage, aValues ) 96 | ConOut( Now() + ' [SUCCESS] ' + ANSI_LIGHT_GREEN + cText + ANSI_END ) 97 | Return ::LogToFile( 'SUCCESS', cText ) 98 | --------------------------------------------------------------------------------