Forum Discussion

konerus's avatar
konerus
Adept
4 years ago

Is logging possible in Khoros?

Is there a way to log information of User Id and the kind of a variable when it hits certain code?

  • I suppose it depends on what kind of logging you're hoping to accomplish.

    For example, one option is to use the FreeMarker logging utilities if you're wanting some server-side logging to occur.

    Alternatively, if you are wanting to have some client-side logging for troubleshooting/debugging purposes in the browser console (i.e. Developer Tools > Console), then you can leverage jQuery to do this.

    In our environment, I created a macro called custom.macro.logging.ftl to simplify client-side logging in our customizations, which looks like this:

    <#------------------------------------------------
      Functions and Macros for Logging and Debugging
      Created By:           Jeff Shurtliff
      Last Modified By:     Jeff Shurtliff
      Last Modified Date:   2020-11-03
    ------------------------------------------------->
    
    <#-------------------- Macro: consoleLog -------------------->
    <#-- This macro prints an entry to the browser JavaScript console (i.e. console.log) -->
    <#macro consoleLog logEntry="">
      <@liaAddScript>
        ; (function ($) {
          console.log("${logEntry}");
        })(LITHIUM.jQuery);
      </@liaAddScript>
    </#macro>
    
    
    <#-------------------- Macro: consoleError -------------------->
    <#-- This macro prints an error to the browser JavaScript console (i.e. console.error) -->
    <#macro consoleError logEntry="">
      <@liaAddScript>
        ; (function ($) {
          console.error("${logEntry}");
        })(LITHIUM.jQuery);
      </@liaAddScript>
    </#macro>
      
    
    <#-------------------- Macro: consoleDebug -------------------->
    <#-- This macro prints a debug message to the browser JavaScript console (i.e. console.debug) -->
    <#macro consoleDebug logEntry="">
      <@liaAddScript>
        ; (function ($) {
          console.debug("${logEntry}");
        })(LITHIUM.jQuery);
      </@liaAddScript>
    </#macro>
    
    
    <#-------------------- Macro: consoleInfo -------------------->
    <#-- This macro prints an info message to the browser JavaScript console (i.e. console.info) -->
    <#macro consoleInfo logEntry="">
      <@liaAddScript>
        ; (function ($) {
          console.info("${logEntry}");
        })(LITHIUM.jQuery);
      </@liaAddScript>
    </#macro>
    
    
    <#-------------------- Macro: consoleWarn -------------------->
    <#-- This macro prints a warning message to the browser JavaScript console (i.e. console.warn) -->
    <#macro consoleWarn logEntry="">
      <@liaAddScript>
        ; (function ($) {
          console.warn("${logEntry}");
        })(LITHIUM.jQuery);
      </@liaAddScript>
    </#macro>

    Then in my other custom components/macros/functions I am able to easily generate log entries by importing the macro file and calling one of the macros above, as demonstrated below.

    <#-- Import the macro file -->
    <#import 'custom.macro.logging' as logging />
    
    <#-- Attempt to get the user ID -->
    <#attempt>
      <#assign userId = user.id />
      <@logging.consoleDebug "The User ID is ${userId}" />
    <#recover>
      <@logging.consoleError "Encountered an exception while retrieving the User ID" />
    </#attempt>

    Then while testing in the browser you can monitor the Console tab in Developer Tools and have a better understanding of what might be failing and why.

    (I like to include the text "Encountered an exception while..." in error messages within the <#recover> directive because it reminds me that I should check out the Toolbox to see what the verbose FreeMarker error was so I can troubleshoot even further.)

    I hope this helps!

    • jeffshurtliff's avatar
      jeffshurtliff
      Boss

      I realized I forgot to comment on the other part of your question about getting "the kind of a variable" which I assume you mean the data type of a variable, e.g. string, hash, sequence, etc.

      FreeMarker has some quite a few built-ins to check the data type of a variable, which are explained in the official documentation.

      However, to avoid having to call a whole bunch of built-ins whenever I want to know the data type of one of my variables, I created the function below which really comes in handy.

      <#-------------------- Function: checkDataType -------------------->
      <#-- This function returns the data type of the object -->
      <#function checkDataType object simple=false>
        <#local dataType = "unknown" />
        <#attempt>
          <#if object?is_string>
            <#local dataType = "string" />
          <#elseif object?is_number>
            <#local dataType = "number" />
          <#elseif object?is_boolean>
            <#local dataType = "boolean" />
          <#elseif object?is_sequence>
            <#local dataType = "sequence" />
            <#if simple?? && !simple>
              <#attempt>
                <#local subType = checkDataType(object[0]) />
                <#if subType?? && subType != "unknown">
                  <#local dataType = dataType + "+${subType}" />
                </#if>
              <#recover>
                <@logging.consoleError "Encountered an exception when attempting to determine data subtype for object" />
              </#attempt>
            </#if>
          <#elseif object?is_hash>
            <#if simple?? && !simple && object?is_hash_ex>
              <#local dataType = "extended_hash" />
            <#else>
              <#local dataType = "hash" />
            </#if>
          <#elseif object?is_date_like>
            <#local dataType = "date_like" />
          <#elseif object?is_method>
            <#local dataType = "method" />
          <#elseif object?is_macro>
            <#local dataType = "macro_function" />
            <#--  Correct syntax for checking marcos is checkDataType(macro) instead of checkDataType(<@macro></@macro>) -->
            <#--  Correct syntax for checking functions is checkDataType(myFunc) instead of checkDataType(myFunc()) -->
          </#if>
        <#recover>
          <@logging.consoleError "Encountered an exception when attempting to determine data type for object" />
        </#attempt>
        <#return dataType />
      </#function>

      This way I can use the function to quickly identify the data type and take action accordingly, such as in the example below.  (I keep the function in a macro file called custom.macro.common.utils.ftl which is why you see it referenced as such below.)

      <#-- Import common utilities -->
      <#import 'custom.macro.common.utils' as commonUtils />
      
      <#assign someVar = 12345 />
      
      <#-- Ensure the variable below is a string -->
      <#assign dataType = commonUtils.checkDataType(someVar) />
      <#if dataType == 'number' || dataType == 'boolean'>
        <#assign someVar = someVar?c />
      </#if>
      
      This is my variable: ${someVar}

      Hope this helps!

    • konerus's avatar
      konerus
      Adept

      Thank you for the quick response Jeff. I am looking at server side logging. When we use the utils where are the logs stored and how can I get a copy of it?

      • I’m actually not entirely sure, to be honest, but my assumption would be that the FreeMarker logs only appear in Toolbox, in the components (or the Studio preview) when they fail or have warnings in the Stage environment, and also in the backend server logs that only Khoros employees can access. 

        So if you’re looking for more real-time logging then you may need to get a little creative with a custom endpoint and something like a RabbitMQ log broker, but then you’d of course have to be careful not to accidentally chew up your quota of third-party/external API calls. 

        If anyone else has figured out a good way to do logging then I’m hoping they’ll chime in, but this is unfortunately the most I’ve been able to come up with thus far.