Skip to content

Logger

Introduction

The RFLogger class of the rf_logger module provides optional log handling for your integration. This should only be used if your integration client does not already have some log handling capability configured.

If log handling is already configured for your integration, simply log the normal way in each of your python files:

1
2
3
4
import logging

LOG = logging.getLogger(__name__)
LOG.info('this is a log message')

All modules in PSEngine log messages this way, so logging from PSEngine is available in whichever handler you use.

Importing and using RFLogger is only necessary when you do not already have any other code to create handlers that output log messages to file or terminal.

See the API Reference for internal details of the module.

Logging Tree Example

The following shows the logging tree after importing and initializing RFLogger. Note the file handlers at the psengine module and root levels, and the console handler at the root level. The handlers are what actually output messages to file and the terminal.

1
2
3
4
5
from logging_tree import printout
from psengine.logger import RFLogger

log = RFLogger().get_logger()
printout()

Prints:

<--""
   Level WARNING
   Handler RotatingFile '/examples/logs/root_recfut.log' maxBytes=20971520 backupCount=5
     Formatter fmt='%(asctime)s,%(msecs)03d [%(threadName)s] %(levelname)s [%(module)s] %(funcName)s:%(lineno)d - %(message)s' datefmt='%Y-%m-%d %H:%M:%S'
   Handler Stream <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>
     Formatter fmt='%(asctime)s,%(msecs)03d %(levelname)s [%(module)s] - %(message)s' datefmt='%Y-%m-%d %H:%M:%S'
   |
   o<--"asyncio"
   |   Level NOTSET so inherits level WARNING
   |
   o<--"charset_normalizer"
   |   Level NOTSET so inherits level WARNING
   |   Handler <NullHandler (NOTSET)>
   |
   o<--[concurrent]
   |   |
   |   o<--"concurrent.futures"
   |       Level NOTSET so inherits level WARNING
   |
   o<--[dotenv]
   |   |
   |   o<--"dotenv.main"
   |       Level NOTSET so inherits level WARNING
   |
   o<--[jsonpath_ng]
   |   |
   |   o<--"jsonpath_ng.jsonpath"
   |   |   Level NOTSET so inherits level WARNING
   |   |
   |   o<--"jsonpath_ng.lexer"
   |   |   Level NOTSET so inherits level WARNING
   |   |
   |   o<--"jsonpath_ng.parser"
   |       Level NOTSET so inherits level WARNING
   |
   o<--"psengine"
   |   Level INFO
   |   Handler <NullHandler (NOTSET)>
   |   Handler RotatingFile '/examples/docs/logs/psengine_recfut.log' maxBytes=20971520 backupCount=5
   |     Formatter fmt='%(asctime)s,%(msecs)03d [%(threadName)s] %(levelname)s [%(module)s] %(funcName)s:%(lineno)d - %(message)s' datefmt='%Y-%m-%d %H:%M:%S'
   |   |
   |   o<--"psengine.helpers"
   |       Level NOTSET so inherits level INFO
   |
   o<--"requests"
   |   Level NOTSET so inherits level WARNING
   |   Handler <NullHandler (NOTSET)>
   |
   o<--"urllib3"
       Level NOTSET so inherits level WARNING
       Handler <NullHandler (NOTSET)>
       |
       o<--"urllib3.connection"
       |   Level NOTSET so inherits level WARNING
       |
       o<--"urllib3.connectionpool"
       |   Level NOTSET so inherits level WARNING
       |
       o<--"urllib3.poolmanager"
       |   Level NOTSET so inherits level WARNING
       |
       o<--"urllib3.response"
       |   Level NOTSET so inherits level WARNING
       |
       o<--[urllib3.util]
           |
           o<--"urllib3.util.retry"
               Level NOTSET so inherits level WARNING

Examples

Warning

The following examples demonstrate how to use this module. Be sure to add appropriate error handling as needed; all possible errors for each method or function are listed in the API Reference page.

Additionally, you must configure the RF_TOKEN environment variable before getting started. For instructions, see Learn.

1: Use a PSEngine module when another SDK has logging enabled

In this example, we create a Logger instance from the logging standard library to emulate the fact that you might have configured your own logging handler. In this case, there is nothing else that needs to be done before using PSEngine.

Tip

In this example, the call to logging.basicConfig creates the necessary handler for you to see output on the terminal. If a handler is not set up, nothing in this example will actually be logged to file or the terminal. Learn more about python log handlers here.

import logging

from psengine.enrich import LookupMgr

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)

mgr = LookupMgr()
ip = mgr.lookup('8.8.8.8', 'ip')

log.info(ip)

If you set the logging level to DEBUG, you will see all the internal PSEngine debug logging.

DEBUG:psengine.base_http_client:Creating an HTTP client session
DEBUG:psengine.enrich.lookup_mgr:Called LookupMgr.lookup(8.8.8.8, ip, fields='None')
DEBUG:psengine.enrich.lookup_mgr:Called LookupMgr._fetch_data(entity='8.8.8.8', entity_type='ip', fields="['timestamps', 'entity', 'risk']")
DEBUG:psengine.rf_client:Called RFClient.request(get, https://api.recordedfuture.com/v2/ip/8.8.8.8, params="{'fields': 'timestamps,entity,risk'}")
DEBUG:psengine.base_http_client:Called BaseHTTPClient.call(method='get', url='https://api.recordedfuture.com/v2/ip/8.8.8.8', data='None', params="{'fields': 'timestamps,entity,risk'}")
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.recordedfuture.com:443
DEBUG:urllib3.connectionpool:https://api.recordedfuture.com:443 "GET /v2/ip/8.8.8.8?fields=timestamps%2Centity%2Crisk HTTP/1.1" 200 None
DEBUG:psengine.base_http_client:HTTP Status Code: 200
DEBUG:psengine.base_http_client:BaseHTTPClient.call ended with return value '<Response [200]>'
DEBUG:psengine.rf_client:RFClient.request ended with return value '<Response [200]>'
DEBUG:psengine.enrich.lookup_mgr:LookupMgr._fetch_data ended with return value 'EnrichedIP: 8.8.8.8, Risk Score: 0, Last Seen: 202'
DEBUG:psengine.enrich.lookup_mgr:LookupMgr.lookup ended with return value 'EnrichedIP: 8.8.8.8, Risk Score: 0, Last Seen: 202'
INFO:__main__:EnrichedIP: 8.8.8.8, Risk Score: 0, Last Seen: 2025-09-01 09:44:16

Any log entries of a higher level, like ERROR or CRITICAL, will be shown as well.

2: Use RFLogger in combination with another PSEngine module

In this example, we have the same code, except for the logger definition, which is via RFLogger. The default RFLogger behaviors are:

  • to log to the console and to a file under the logs/ directory in the same location where your script is running
  • to log everything from INFO and above
  • formatted log entries:
    2025-09-01 13:27:21,297 [MainThread] INFO [example_2] <module>:9 - EnrichedIP: 8.8.8.8, Risk Score: 0, Last Seen: 2025-09-01 11:58:39
    
  • maintain log propagation for other loggers

All of these parameters can be configured during RFLogger initialization.

1
2
3
4
5
6
7
8
9
from psengine.enrich import LookupMgr
from psengine.logger import RFLogger

log = RFLogger().get_logger()

mgr = LookupMgr()
ip = mgr.lookup('8.8.8.8', 'ip')

log.info(ip)