Communications · Foundation · Libraries · Uncategorized

GrijjyCloudLogger, remote logging for Windows, iOS, Android, macOS and Linux

GrijjyCloudLogger is a remote logging tool that allows you to send log messages over the Intranet or Internet from Windows, Linux, iOS, Android and macOS devices to a viewer running on Windows. Besides sending messages along with any data, it has numerous features including custom live watches, remote live views of objects, tracking live memory usage, object allocations, growth leaks and more.

Here at Grijjy we use this tool on a daily basis to help us diagnose run-time related issues with our applications running on various platforms. Our logger helps us easily examine the run-time state of our application running on iOS and Android devices, which can be difficult using the debugger or mobile platform specific logging features. We developed this utility because we needed a high-performance remote logger that worked on all platforms and operating systems and we wanted unified, run-time debug related capabilities like memory and object tracking from these respective platforms.

Being able to analyze the run-time of your application using your Windows desktop, where your IDE already resides, is a powerful tool in your arsenal as a developer. We hope you find it useful.

The GrijjyCloudLogger is built upon our ZeroMQ Majordomo implementation that allows you to create powerful, lightweight, distributed applications that can route messages over any network, including the Internet. It is extremely fast over the network and can handle numerous connected developers simultaneously. It also uses our Google Protocol Buffers implementation that allows us to encapsulate extensible and arbitrary data and transport the data using efficient payloads.

Getting Started

To use GrijjyCloudLogging in your Delphi project all you need to do is include the Grijjy.CloudLogging unit in your uses list. You should also run the GrijjyLogBroker.exe to route messages and the GrijjyLogViewer.exe to view messages. These binaries are included in our GitHub repository.

uses Grijjy.CloudLogging;

To send messages simply use one of the many provided GrijjyLog.Send() methods.

GrijjyLog.Send('String value', 'Foo');

Sending data using Grijjy Cloud Logger

The Grijjy Cloud Logger provides various overloaded Send() methods to remotely send data over the network to the GrijjyLogViewer. Besides the basic data types, the Grijjy Cloud Logger support sending of advanced data types including TStrings, Memory Pointers, TBytes and even TObjects.

Each of the various Send() methods has common parameters, namely AMsg parameter which is always a string that is displayed in the Log Viewer Messages frame. The AValue is the actual data of various overloaded types. You can also send an optional TgoLogLevel parameter indicating the severity of the message. Lastly an optional parameter called the Service name. The Service name indicates who is sending the message so the Log Broker can properly route the message to the intended Log Viewer (more on this topic later).

Common types

Most of the basic data types are supported by passing the data directly to the Send() method.

GrijjyLog.Send('String value', 'Foo'); // strings
GrijjyLog.Send('Integer value', 42); // integer
GrijjyLog.Send('Boolean value', True); // boolean
GrijjyLog.Send('Float value', Pi); // extended

TStrings

TStrings can be sent over the cloud logger as well by simply constructing a TStringList and calling the overloaded Send() method.

var
  S: TStringList;
begin
  S := TStringList.Create;
  try
    S.Add('Foo');
    S.Add('With Spaces');
    S.Add('With, Commas');
    S.Add('With "Quotes"');
    S.Add('With ''Quotes''');
    S.Add('Width , "every", ''thing''');
    GrijjyLog.Send('TStrings value', S, TgoLogLevel.Warning);
  finally
    S.Free;
  end;
end;

TBytes

Grijjy Cloud Logger provides several methods to display blocks of memory. In this example we send a TBytes object to the remote viewer.

var
  Bytes: TBytes;
begin
  Bytes := TEncoding.UTF8.GetBytes
    ('The Quick Brown Fox Jumps Over The Lazy Dog');
  GrijjyLog.Send('TBytes value', Bytes, TgoLogLevel.Warning);
end;

Data Pointers

The second approach to sending blocks of memory over the cloud logger is to use the Send() method with a Pointer to a memory location and a Size in bytes.

var
  Bytes: TBytes;
  I: Integer;
begin
  SetLength(Bytes, 997);
  for I := 0 to Length(Bytes) - 1 do
    Bytes[I] := Random(256);
  Bytes[10] := 0;
  GrijjyLog.Send('Memory value', @Bytes[0], Length(Bytes), TgoLogLevel.Warning);
end;

TObject

TObjects can be sent along with all of the objects fields and properties. You can choose whether only public and published fields are shown or all protected and private fields as well. Optionally you can also choose how many subfield levels are sent for the various fields and properties.

GrijjyLog.Send('Object value', Self, mvPublic, 4, TgoLogLevel.Warning);

Note that this can potentially be a slow and bandwidth-intensive call since RTTI is used to query the object, and it may result in large data loads depending on the AMinVisibility and AMaxNesting parameters.

Nested methods

The GrijjyCloudLogger contains method helpers that allow you to more easily view when methods are entered and exited. EnterMethod logs the start of a method block. Subsequent Send() calls to Log will be treated as part of this method, until ExitMethod is called.

var
  Foo: TSampleFoo;
begin
  Foo := TSampleFoo.Create;
  try
    GrijjyLog.EnterMethod(Self, 'ButtonMethodClick');
    GrijjyLog.Send('Inside TFormMain.ButtonMethodClick', TgoLogLevel.Info);
    Foo.SomeMethod;
    GrijjyLog.ExitMethod(Self, 'ButtonMethodClick');
  finally
    Foo.Free;
  end;
end;

Information, warning and error messages

All the Send() methods support an optional TgoLogLevel parameter indicating the severity of the message. Additionally there are messages specifically for sending TgoLogLevel notifications.

  GrijjyLog.Send('Sample Error Message', TgoLogLevel.Error);
  GrijjyLog.Send('Sample Info Message', TgoLogLevel.Info);
  GrijjyLog.Send('Sample Warning Message', TgoLogLevel.Warning);

A special method called SetLogLevel() allows you to control which messages are displayed in the GrijjyLogViewer based upon the current log level The default log level is Info in DEBUG mode and Warning in RELEASE mode. With Info level, all messages are logged. With Warning level, only warning and error messages are logged and with Error level, only error messages are logged.

Memory and object tracking

Besides the cross-platform remote logging capabilities, the GrijjyCloudLogger contains integrated features that help you understand the state of your application at run-time. The first of these features is the memory and object tracker.

To enable the object tracker, you need to add the Grijjy.CloudLogging.InstanceTracker unit to the uses clause of your project (.dpr) file, preferably as the very first unit:

program MyProgram;

uses
  Grijjy.CloudLogging.InstanceTracker,
  ...other units...

By adding this unit, your application will keep track of most allocated objects (using techniques described in our Cross-Platform Code Hooking blog article), so it can provide this information to the log viewer. Since this adds some overhead, this functionality is only enabled in DEBUG builds. In RELEASE builds, the InstanceTracker unit will act as an empty unit.

Each time you click Update in the GrijjyLogViewer, it will provide a real-time snapshot of the allocated objects from your running application and the memory consumed by those objects. This is very useful in determining if particular actions in your run-time application are actually allocating and deallocating objects as you expect. The tracker also displays the Delta between each Update and the Max or peak values.

Live watches

The Live watches feature of the GrijjyCloudLogger allows you to create custom data that can be sent over the network to the GrijjyLogViewer at run-time.

To create a live watch, you only need to subscribe a TMessage in your application and add your custom data to the TgoLiveWatchesMessage. The GrijjyCloudLogger will automatically handle the data transport, routing and viewing.

To start sending live watches to the remote log viewer, simply subscribe the TgoLiveWatchesMessage message.

TMessageManager.DefaultManager.SubscribeToMessage(TgoLiveWatchesMessage, HandleLiveWatches);

In your TgoLiveWatchesMessage listener you populate the TgoLiveWatchesMessage with one or more message and data elements.

procedure TMyClass.HandleLiveWatches(const Sender: TObject;
  const M: TMessage);
var
  Msg: TgoLiveWatchesMessage absolute M;
begin
  Assert(M is TgoLiveWatchesMessage);
  Msg.Add('Custom Watch', TrackBar.Value, 1);
end;

Then in the GrijjyLogViewer you click Update to retrieve the current state of any live watches in your remote app.

Grijjy Log Broker

The GrijjyLogBroker routes log messages between senders and viewers. By default the Broker is configured to listen on tcp://localhost:7337. When you use Grijjy.CloudLogging in your project you need to call the GrijjyLog.Connect() method and provide the address of the Broker. You also can provide an optional service name. If you intend to share the Broker across your development organization, each developer should use a unique service name when connecting to the Broker. This service name is provided to both the GrijjyLog.Connect when sending and receiving messages and to the GrijjyLogViewer in the connection settings.

Grijjy Log Viewer

The GrijjyLogViewer displays messages that are sent from your app. The Service name in the Connection Settings of the GrijjyLogViewer should match the same Service name you provided to the Grijjy.CloudLogger when calling the Connect() method. Using this technique, all developers in your organization can share the same GrijjyLogBroker.

Installing the GrijjyCloudLogger

GrijjyCloudLogger is available in the Embarcadero GetIt Package Manager in RAD Studio, otherwise to install the GrijjyCloudLogger you need to install a few repositories and add some Delphi library search paths.

1. Download the following GitHub repositories,

2. Add the GrijjyFoundation source folder to your Delphi library path for all platforms.

3. Add the DelphiZeroMQ source folder to your Delphi library path for all platforms.

4. Run the GrijjyLogBroker.exe and the GrijjyLogViewer.exe located under the /GrijjyCloudLogger/Bin folder.

If your app is running on Windows, macOS and Linux you must deploy the ZeroMQ library for those respective platforms with your app. Those library binaries are pre-built and are located under the folder /DelphiZeroMQ/Lib. For Android and iOS we use pre-built static libraries that are automatically linked into your application.

Firemonkey Example

We included an example that demonstrates how to Send() GrijjyCloudLogger messages from all platforms supported by FireMonkey currently, including Windows, Android, iOS and macOS. The ExampleLogClient.FMX is provided in our GitHub repository for the GrijjyCloudLogger.

Console Example for Windows and Linux Services

For Console oriented platforms like Linux and server-side applications we also include an ExampleLogClient.Console in our GitHub repository for the GrijjyCloudLogger.

Using GrijjyCloudLogger with development teams

While it is perfectly acceptable to run the GrijjyLogBroker, GrijjyLogViewer and your app on the same Windows computer as a single developer, you may want to consider sharing your installation with the entire development team. There are many reasons why this may be beneficial.

First off all, the ZeroMQ Majordomo protocol that the GrijjyCloudLogger is based upon, takes care of routing issues for you automatically. All sender apps using the Grijjy.CloudLogging unit and all log viewer using GrijjyLogViewer connect to the Broker directly. If you run the Broker on your network, everyone simply routes the same TCP/IP address and port, they just use different Service names. However, your network manager could create a network port mapping to the GrijjyLogBroker so that your entire team could use the same Broker whether you were local to the network or remote and your messages would route from all your apps to any of your viewers automatically with a single firewall network mapping.

Another approach is to simply install the GrijjyLogBroker on a computer on the Internet in the cloud. Everyone on the local network or those connecting over the Internet would route the same TCP/IP address and port and no firewall changes are required.

ZeroMQ and the Majordomo Protocol

As we mentioned earlier, the GrijjyCloudLogger is built using our implementation of the ZeroMQ Majordomo protocol. It demonstrates one of many possible solutions you can create with the powerful ZeroMQ distributed framework.

Another really nice feature of ZeroMQ, is that it is mostly protocol agnostic. In the example we connect using tcp://localhost:7337. However ZeroMQ supports many other protocols including ipc:// (interprocess communications), inproc:// (intraprocess) and more.

ZeroMQ is an excellent choice for many iOT and performance oriented messaging models. If you are interested in this topic, please see our related article on ZeroMQ.

Conclusion

We hope you find our remote logger useful in your everyday development efforts. The Grijjy team is considering adding other features for the future including showing full stack traces of each object allocation on most platforms, trapping and remote logging of exceptions on various platforms and more.

The example contained here depends upon part of our Grijjy Foundation library.

The source code and related example repository are hosted on GitHub at https://github.com/grijjy/GrijjyCloudLogger.

License

GrijjyCloudLogger, Grijjy.CloudLogging, GrijjyLogBroker, GrijjyLogViewer and related classes along with the example programs are licensed under the Simplified BSD License. See License.txt for details.

6 thoughts on “GrijjyCloudLogger, remote logging for Windows, iOS, Android, macOS and Linux

  1. Allen, you work is always very good, i m a fan !! It’s deserve to be commercial product …

    Just few remarks :
    1/ in the memory usage panel could be good to be able to sort by count/previous/delta/max
    2/ in the memory usage panel could be good to have also a total memory colum next to count column (to know how much memory use a group of particular object)
    3/ In the memory usage panel, it’s seam the memory used by openGL texture is not counted in the total allocated memory 😦 It’s very important to know this under android / ios but not sure if it’s possible to do …

    4) less important, could be good to be able to put memory usage panel at full screen (ie: remove watch, message panel)

    5) even less important, in the doc, you can add a warn to say to not forget to put libzmq-win64.dll and libzmq-win32.dll in the path (else on delphi i have a crash of the app without any warning and i don’t know that it’s because a dll is missing)

    Liked by 1 person

    1. Just one remark about the Texture memory used, One easy way to get the total memory used by OpenGL texture will be to do for each TTexture object (or descendant) :

      totalOpenGLTextureMemoryUsed := 0;
      for all texture do
      totalOpenGLTextureMemoryUsed := totalOpenGLTextureMemoryUsed + (currtexture.width*currtexture.height*currtexture.BytesPerPixel) …..

      Like

      1. You can use the “Live Watches” feature to add this information to the log viewer. In you app, subscribe to the TgoLiveWatchesMessage message, and in the message handler, add a custom watch with the value you calculated. See Frame.Watches in the example client on how to do this.

        Like

    2. Those are all great suggestions! Thank you for the feedback. We have discussed making a commercial version, we just not sure what people would pay for other features, the source and support. I will see what we can do about checking for the library instead of bombing out for the next update as well. Also, I will ask my colleague Erik, who knows all the ins and outs of OpenGL on various platforms about your OpenGL question. Is that for Windows only? Look for an update in the near future.

      Like

      1. thanks Allen … no opengl is mostly for android/ios (on windows we don’t use opengl). But i think the most easy way will be just to count the number of TTexture object and calculate their sizes.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s