lunes, 19 de noviembre de 2018

BinaryMessage

Si necesita serializar datos en formato binario, BinaryMessage puede ayudar.

En computación, serialización es el proceso de convertir datos abstractos en datos almacenados (en forma persistente) o datos para trasmitirlos a otro destino (servidor de red o dispositivo).

He diseñado BinaryMessage basándome en productos como protocol-buffersmsgpack; debido a la necesidad de trasmitir datos en un producto que estoy terminando, esremo, necesitaba serializar, trasmitir y de-serializar datos al recibirlos de una forma lo más automática posible.

Tal vez pueden pensar, "hey!, para que crear la rueda si ya hay xml o json?". Pues a parte de javascript que tiene un tipo de datos json, en todos los demás lenguajes hay unas clases para manejar XML o JSON, por lo general, creamos clases para manejar datos y queremos que esas clases las podamos serializar y con XML o JSON tenemos que "mapearlas" a esos tipos de objetos.

Además el tamaño de los objetos serializados son menores, pero, y por que no comprime los datos? Cuando los datos son pequeños (como los mensajes de un chat) los datos comprimidos son mayores a los datos originales. Y cuando los productos que brindamos están en servicios en la nube donde cobran el ancho de banda de entrada/salida, los bytes cuentan!

Otro punto es utilizar el editor para ayudarnos con auto-completado ya que son propiedades de clases y con XML y JSON no podemos usar auto-completar a propiedades.

Aunque he diseñado BinaryMessage (como los otros productos) para que sea plataforma-neutral (que se pueda usar en cualquier plataforma, Win, MacOS, Linux, raspberry-Pi) y lenguaje-neutral (c, c++, etc) en este momento la especificación es open y está codificado en Xojo.

Para usar BinaryMessage una clase (si no es creada con el compilador o usando la clase BinaryMessage.Parser) debe heredarse de BinaryMessage.Message adicionar attributos y usar .WriteTo a un MemoryBlock/BinaryStream para serializar y utilizar .ReadFrom desde un MemoryBlock/BinaryStream para de-serializar.


Ejemplo serializar:

Dim john As New People
john.id= 5
john.name= "John Do€"
john.email= "john@server.com"
john.address= Array("1600 Pennsylvania Avenue", "935 Pennsylvania Avenue")

Dim phone1 As New PhoneNumber
phone1.number= "123 4567"
phone1.type= 1
phone1.flags= Array(4900000000, 5000000000, 80000000000)
john.phones.Append phone1

Dim phone2 As New PhoneNumber
phone2.number= "555 5555"
phone2.type= 2
john.phones.Append phone2

Dim phone3 As New PhoneNumber
phone3.number= "999 5555"
phone3.type= 3
john.phoneX= phone3

Dim prefs As New Dictionary
prefs.Value("one")= 1
prefs.Value(2)= "two"
prefs.Value("three")= True
prefs.Value(3.5)= 49098236671
john.prefs= prefs

mbMessage= New MemoryBlock(0)

john.WriteTo mbMessage


Ejemplo de-serializar:


BinaryMessage.Message.RegisterClass GetTypeInfo(PhoneNumber)

Dim john As New People
john.ReadFrom mbMessage


Si desea más información sobre la especificación o desea probar BinaryMessage, por favor comente o escríbame.

Hasta la próxima.



miércoles, 14 de noviembre de 2018

LoggerFactory

En un reciente proyecto en el que he estado trabajando este año, necesité un buen sistema de log (bitácora o entradas en un diario), como de costumbre busqué si había alguno para Xojo/Real basic, al no encontrar uno que fuera flexíble y no "encadenado" a un particular componente o recurso, decidí crear mi propio logger: LoggerFactory.

A propósito, en computación un Logger es un software que genera mensajes y opcionalmente los almacena para reportar o analizar luego, puede encontrar información de sus inicios aquí y más aquí, un buen ejemplo de donde o para qué utilizarlo pueden ser los logs de servidores, en especial los servidores web; ellos almacenan mucha información de los clientes para después analizarlos con servicios muy comunes en web-hostings. A propósito, talvez se preguntaran: para que utilizar los logs de servidor si tengo otras herramientas como google analitics? pues esas otras herramientas funcionan sólo en navegadores de "buen comportamiento" como chrome, pero existen miles de herramientas para acceder a servidores web para sólo ver las cabeceras del archivo html o sin sus links, imágenes, javascripts, css, etc, o cuando escribe un "downloader" usando HTTPSocket.

Como regla general se deben monitorear los logs para optimizar, corregir o defenderse contra ataques, de ahi la importancia de un buen Logger.


Debería cumplir estas (entre otras) características:
  1. Fácil de usar
  2. Extensible
  3. Asincrónico
  4. Varias salidas por nivel
  5. Personalizar formato de salida

Basandome an algunos muy buenos loggers, entre otros java.util.logging y MacOS Logging cree LoggerFactory, (existe LoggerFactory para java, pero es más un wrapper (envoltorio) a otros loggers) el cual resultó ser bastante sencillo y corto de codificar, en realidad menos de 900 líneas de código, con las características que quería para mi proyecto y además me pudiera servir para cualquier otro proyecto.

Ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  Dim logger As LoggerFactory.Logger= LoggerFactory.Get(CurrentMethodName)
  '...
  logger.Info "this is a info"
  '...
  logger.Debug "this is a debug"
  '...
  logger.Trace "this is a trace"
  '...
  logger.Error "this is a error"
  '...
  logger.Warn "this is a warning"
  '...
  logger.Fatal "this is a fatal"


LoggerFactory usa estos niveles: ALL, DEBUG, ERROR, FATAL, INFO, TRACE, WARN


Por defecto, LoggerFactory está configurado para consola y System.DebugLog, Funciona sin ninguna modificación en proyectos Consola y Desktop, no he probado en web. Puede configurarse en cualquier momento de la aplicación pero lo normal es hacerlo al inicio, ejemplo:


1
2
3
  LoggerFactory.Adders.Append New LoggerFactory.ConsoleAdder(LoggerFactory.TypeLevel.ALL)

  LoggerFactory.Adders.Append New LoggerFactory.FileLogAdder(LoggerFactory.TypeLevel.ALL)

En el anterior código, he configurado un "Adder" para consola y otro para archivo, aquí es donde explico lo de Adders, Adders es la forma para "adicionar" salidas (mensajes) configurando el nivel, o si es salida a un archivo, características del archivo, por ejemplo. Los Adders básicos son:

  • ConsoleAdder: Salidas por consola (sólo en proyectos consola, obiamente)
  • SystemDebugLogAdder: El típico System.DebugLog
  • SystemLogAdder: El System.Log
  • FileLogAdder: Salidas a un archivo

Lo interesante es que podemos, por ejemplo, configurar salidas de tipo DEBUG a SystemDebugLogAdder, además salidas de tipo ERROR a un archivo, cambiar el formato de salida o inclusive crear mis propios Adders, lo único que tengo que hacer es crear una sub-clase de LoggerFactory.Adder y sobreescribir el método "Log"; con eso puedo crear una salida a, por ejemplo una base de datos o un servicio web; sólo configurando los Adders, sin cambiar nada más.


Una característica interesante de los Adders es que puedo configurar el formato de salida, así:


1
2
3
  Dim addFile As New LoggerFactory.FileLogAdder(LoggerFactory.TypeLevel.ALL)

  addFile.Format= "%d"+ SEP+ "%t"+ SEP+ "%l"+ SEP+ "%c"+ SEP+ "[%a]"+ SEP+ "%m"

Donde las etiquetas pueden ser:

%d: Date
%t: Time
%l: Level
%c: Counter
%u: SubSystem
%a: Category
%i: ID
%o: ObjectID
%e: EventType
%m: Message

Y "SEP" es una propiedad del adder, por defecto es TAB [Chr(9) en ASCII] y también se puede configurar.


Además el FileLogAdder puede ser configurado con "roll over" o re-iniciar, con la propiedad FileLogAdder.FileType y puede ser: al mismo archivo, anual, mensual, diario, por hora, o por el tamaño (por ejemplo que el archivo log sea de máximo 10M (10 megabytes) de tamaño y automáticamente asigne nombres secuenciales a los archivos)


Por último, hablemos de "Formatter", cuando escribimos mensajes, normalmete necesitamos adicionar valores de variables (números, por ejemplo) y darles un formato, por ejemplo en una variable llamada "miVariable" tenemos un valor de 1250 y queremos mostar un texto como: "Valor: 1,250", en xojo existe la función Str() o Format() y podemos asignar a una variable de tipo String, sin embargo puedo hacerlo también de la siguiente manera:


1
2
3
Dim miVariable As Single= 1250

logger.Debug "Valor: %-###,###.##f", miVariable // Salida: "Valor: 1,250"

Con la ventaja que los textos son más legibles y pueden ser almacenados en una constante para, por ejemplo, localizarlos en otro idioma.

Otros ejemplos:


1
2
3
4
5
6
  Dim n1 As UInt64= 202002021101
  Dim n2 As Double= 12.3456
  Dim n3 As Single= -12345.6789
  Dim s1 As String= "world"
  logger.Debug "i = %2d, n1= %u, n2= %3.5f, hello %s, single= %-###,##0.0###f - %{pub}s", _
  i, n1, n2, s1, n3, "test"

Las etiquetas pueden ser:

%d: Signed integers
%u: Unsigned integers
%f: Signed doubles/singles
%s: Strings


Otras características interesantes son el manejo de Sub-Sistemas y la precisión de tiempo de hasta nanosegundos, pero eso quedará para otra ocasión, si le interesa o tiene alguna sugerencia o pregunta, por favor contácteme o comente. hasta la próxima.



Gracias a Javier Mendéz por sus recomendaciones.