Using the Logging API

Introduction to the Logging API

The Logging API enables applications to subscribe to real-time log messages from a simulation, query previously logged messages from the MySQL database, and publish messages to their either own log or their GridAPPS-D logs.

subscribe-publish-to-logs

API Communication Channel

As with the Simulation API, the logging API uses both static /queue/ and dynamic /topic/ communication channel names depending on whether the API is being used for real-time simulation logs or historic logs that have already been saved in the database.

For a review of GridAPPS-D topics, see Lesson 1.4.

The correct topic for each Logging API call will be provided in the corresponding section for each API task below.


Message Structure

Logging messages in the GridAPPS-D environment follow the format of a python dictionary or equivalent JSON string with the format below.

{       KEY               VALUE
    "source":               filename,
    "processId":            simulation_id,
    "timestamp":            epoch time number,
    "processStatus":        "STARTED" or "STOPPED" or "RUNNING" or "ERROR" or "PASSED" or "FAILED",
    "logMessage":           string,
    "logLevel":             "INFO" or "DEBUG" or "ERROR",
    "storeToDb":            true or false
}

All of the messages from a single instantiation will have the same format, with the only difference being the logMessage. As a result, it is possible to use the shortcuts available from the GridAPPSD-Python library to build out the repetitive portions of the message and pass just the logMessage string.


GridAPPSD-Python Logging API Extensions

The GridAPPSD-Python library uses several extensions to the standard Python logging library that enable applications to easily create log messages using the same syntax. These extensions support the additional log message formatting required by GridAPPS-D, such as simulation_id, log source, and process status.

The following code block enables the

[ ]:
import logging
import os

os.environ['GRIDAPPSD_APPLICATION_ID'] = 'gridappsd-sensor-simulator'
os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STARTED'
os.environ['GRIDAPPSD_SIMULATION_ID'] = opts.simulation_id

Important! Run this import command *BEFORE* creating the GridAPPS-D connection object gapps = GridAPPSD(...).

Note: If your application is containerized in Docker and registered with the GridAPPS-D platform using the docker-compose file, these extensions will be imported automatically.


Subscribing to Simulation Logs

Similar to the two approaches used to subscribe to simulation measurements discussed in Comparison of Subscription Approaches, it is possible to use either a function or a class definition to subscribe to the simulation logs.

Subscription API Communication Channel

This is a dynamic /topic/ communication channel that is best implemented by importing the GriAPPSD-Python library function for generating the correct topic.

  • from gridappsd.topics import simulation_log_topic

  • log_topic = simulation_log_topic(simulation_id)

Note on Jupyter Notebook environment: In the examples below, the Jupyter Notebook environment does not update definitions of the subscription object or function definitions. As a result, it is necessary to restart the notebook kernel. The gapps connection object definition is included again for convenience in executing the notebook code blocks

[3]:
viz_simulation_id = "paste sim id here"

# Establish connection to GridAPPS-D Platform:
from gridappsd import GridAPPSD

# Set environment variables - when developing, put environment variable in ~/.bashrc file or export in command line
# export GRIDAPPSD_USER=system
# export GRIDAPPSD_PASSWORD=manager

import os # Set username and password
os.environ['GRIDAPPSD_USER'] = 'tutorial_user'
os.environ['GRIDAPPSD_PASSWORD'] = '12345!'

# Connect to GridAPPS-D Platform
gapps = GridAPPSD(viz_simulation_id)
assert gapps.connected
[ ]:
from gridappsd.topics import simulation_log_topic
log_topic = simulation_log_topic(viz_simulation_id)

Note on Jupyter Notebook environment: In the examples below, the Jupyter Notebook environment does not update definitions of the subscription object or function definitions. As a result, it is necessary to restart the notebook kernel. The gapps connection object definition is included again for convenience in executing the notebook code blocks


Subscribing using a Function Definition

The first approach used to subscribe to measurements is to define a function with the correct set of arguments that is then passed to the .subscribe() method associated with the GridAPPPSD() object.

The function does not require a specific name, and is somewhat easier to define and use. However, the arguments of the function need to be named correctly for the GridAPPSD-Python library to process the simulation output correctly.

The format for the function definition is

def myLogFunction(header, message):
    # do something when receive a log message
    # do something else

That function handle is then passed as an argument to the .subscribe(topic, function_handle) method:

[ ]:
def demoLogFunction(header, message):
    timestamp = message["timestamp"]
    log_message = message["logMessage"]

    print("Log message received at timestamp ", timestamp, "which reads:")
    print(log_message)
    print("........................")
[ ]:
gapps.subscribe(log_topic, demoLogFunction)

Subscribinb using a Class Definition

The second approach used to subscribe to simulation logs is to define add a custom method to the same class with __init__ and on_message methods that was created to subscribe to measurements.

Unlike the Simulation API, the Logging API does not require a specific name for the method used to subscribe to log messages.

It is possible to create additional methods in the subscription class definition to enable the app to subscribe to additional topics, such as the simulation log topic, as shown in the example below.

[ ]:
class PlatformDemo(object):
    # A simple class for interacting with simulation

    def __init__(self, simulation_id, gapps_obj):
        # Initialize variables and attributes
        self._gapps = gapps_obj
        self._simulation_id = simulation_id
        # self.foo = bar

    def on_message(self, headers, message):
        # Do things with measurements
        meas_value = message["message"]["measurements"]
        # Do more stuff with measurements

    def my_logging_method(self, headers, message):
        timestamp = message["timestamp"]
        log_message = message["logMessage"]

        print("Log message received at timestamp ", timestamp, "which reads:")
        print(log_message)
        print("........................")
[ ]:
# Create subscription object
demo_obj = PlatformDemo(viz_simulation_id, gapps)

# Subscribe to logs using method
gapps.subscribe(log_topic, demo_obj.my_custom_method)

Publishing to Simulation Logs

The GridAPPSD-Python library enables use of the standard Python logging syntax to create logs, publish them to the GOSS Message Bus, and store them in the MySQL database.

Documentation of the standard Python logging library is available on Python Docs.

It is possible to publish to either local app logs (which are more useful for debugging) or the GridAPPS-D logs (which can be accessed by other applications and should be used for completed applications).

Publishing to Local App Logs

The first approach is to use the default Python logger to write to local app logs by importing the logging library and then use the .getLogger() method from the Python library.

[6]:
import logging

python_log = logging.getLogger(__name__)

Log messages can then be published by invoking the methods

  • python_log.debug("log message")

  • python_log.info("log message")

  • python_log.warning("log message")

  • python_log.error("log message")

  • python_log.fatal("log message")


Publishing to GridAPPS-D Logs

The second approach is to use the GridAPPS-D logs. Importing the python logging library is not necessary. Instead initialize a logging object using the .get_logger() method associated with the GridAPPS-D connection object. Note the difference in spelling of the GridAPPS-D Library and default Python Library methods.

[5]:
gapps_log = gapps.get_logger()

Log messages can then be published by invoking the methods

  • gapps_log.debug("log message")

  • gapps_log.info("log message")

  • gapps_log.warning("log message")

  • gapps_log.error("log message")

  • gapps_log.fatal("log message")


Querying Saved Logs

Log messages published using the Logging API and the GOSS Message Bus are saved to the MySQL database. These log messages can be accessed with a Logging API query.

Log Query API Communication Channel

The query for logs uses a static /queue/ channel that is imported from the GridAPPS-D Topics library.

This topic is used with the .get_response(topic, message) method associated with the GridAPPS-D connection object.

  • from gridappsd import topics as t

  • gapps.get_response(t.LOGS, message)


Structure of the Log Query Message

The first approach to querying with the Logging API is to use a python dictionary or equivalent JSON string that follows formatting similar to the query messages used by all the other GridAPPS-D APIs:

[ ]:
from gridappsd import topics as t

message = {
    "source": "ProcessEvent",
    "processId": viz_simulation_id,
    "processStatus": "INFO",
    "logLevel": "INFO"
}

gapps.get_reponse(t.LOGS, message)

|GridAPPS-D\_narrow.png|