OH3: log.info is not a function

  • Platform information:
    • Hardware: Raspberry Pi 4 Model B Rev 1.1 ; 4GB memory
    • Host: Linux openhabian 5.15.76-v7l+ #1597 SMP Fri Nov 4 12:14:58 GMT 2022 armv7l GNU/Linux
    • Distro: Raspbian GNU/Linux 11 (bullseye)
    • openjdk version “11.0.18” 2023-01-17
    • OpenJDK Runtime Environment (build 11.0.18+10-post-Raspbian-1deb11u1)
    • OpenJDK Server VM (build 11.0.18+10-post-Raspbian-1deb11u1, mixed mode)
  • OH Version: 3.4.3 (Build)
    • Installation method: openHABian
Directories: Folder Name      | Path                        | User:Group
             -----------      | ----                        | ----------
             OPENHAB_HOME     | /usr/share/openhab          | openhab:openhab
             OPENHAB_RUNTIME  | /usr/share/openhab/runtime  | openhab:openhab
             OPENHAB_USERDATA | /var/lib/openhab            | openhab:openhab
             OPENHAB_CONF     | /etc/openhab                | openhab:openhab
             OPENHAB_LOGDIR   | /var/log/openhab            | openhab:openhabian
             OPENHAB_BACKUPS  | /var/lib/openhab/backups    | openhab:openhab

I took the plunge, after two years of contemplation :slight_smile:, to move from ruleDSL to ECMAscript 2021. I did not need to install or change my environment, as I had installed Automation JS two years ago.

openhab> bundle:list | grep JavaScript                                                                                              
268 │ Active │  80 │ 3.4.3                  │ openHAB Add-ons :: Bundles :: Automation :: JavaScript Scripting
309 │ Active │  75 │ 3.4.3                  │ openHAB Add-ons :: Bundles :: Transformation Service :: JavaScript

The script I ran, actually works, but does not log.

const { log } = require('openhab');
rules.JSRule({
  name: "TestSwitch",
  triggers: [triggers.ItemStateChangeTrigger('test_Switch')],
  execute: () => {
    log.info(`test_Switch is ${items.getItem('test_Switch').state}`);
  }
});

The error I am getting is:

2025-04-15 22:20:08.701 [INFO ] [ort.loader.AbstractScriptFileWatcher] - Loading script '/etc/openhab/automation/js/test.js'
2025-04-15 22:20:15.430 [INFO ] [.openhab.automation.openhab-js.rules] - Adding rule: TestSwitch
2025-04-15 22:20:19.914 [ERROR] [enhab.automation.script.file.test.js] - Failed to execute rule TestSwitch-dfe231ee-bca2-40aa-98ce-57e84d42edde: TypeError: log.error is not a function: TypeError: log.error is not a function
2025-04-15 22:20:19.928 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'TestSwitch-dfe231ee-bca2-40aa-98ce-57e84d42edde': Fail to execute action: 1
2025-04-15 22:20:23.301 [ERROR] [enhab.automation.script.file.test.js] - Failed to execute rule TestSwitch-dfe231ee-bca2-40aa-98ce-57e84d42edde: TypeError: log.error is not a function: TypeError: log.error is not a function
2025-04-15 22:20:23.303 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'TestSwitch-dfe231ee-bca2-40aa-98ce-57e84d42edde': Fail to execute action: 1
2025-04-15 22:24:39.818 [INFO ] [ort.loader.AbstractScriptFileWatcher] - Loading script '/etc/openhab/automation/js/test.js'
2025-04-15 22:24:45.876 [INFO ] [.openhab.automation.openhab-js.rules] - Adding rule: TestSwitch
2025-04-15 22:24:50.457 [ERROR] [enhab.automation.script.file.test.js] - Failed to execute rule TestSwitch-9dd9bcf9-9609-4c8e-b004-dc0b2b37492a: TypeError: log.info is not a function: TypeError: log.info is not a function
2025-04-15 22:24:50.464 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'TestSwitch-9dd9bcf9-9609-4c8e-b004-dc0b2b37492a': Fail to execute action: 1
2025-04-15 22:24:52.614 [ERROR] [enhab.automation.script.file.test.js] - Failed to execute rule TestSwitch-9dd9bcf9-9609-4c8e-b004-dc0b2b37492a: TypeError: log.info is not a function: TypeError: log.info is not a function
2025-04-15 22:24:52.616 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'TestSwitch-9dd9bcf9-9609-4c8e-b004-dc0b2b37492a': Fail to execute action: 1

I then changed to the SLF4J logger:

const LoggerFactory = Java.type('org.slf4j.LoggerFactory');
const logger = LoggerFactory.getLogger('org.openhab.TestSwitch');
rules.JSRule({
  name: "TestSwitch",
  triggers: [triggers.ItemStateChangeTrigger('test_Switch')],
  execute: function(event) {
    logger.info("test_Switch changed to " + event.newState);
  }
});

This works, without creating an error.

2025-04-15 22:39:55.902 [INFO ] [ort.loader.AbstractScriptFileWatcher] - Loading script '/etc/openhab/automation/js/test.js'
2025-04-15 22:40:02.605 [INFO ] [.openhab.automation.openhab-js.rules] - Adding rule: TestSwitch
2025-04-15 22:40:06.171 [INFO ] [org.openhab.TestSwitch              ] - test_Switch changed to ON
2025-04-15 22:40:08.739 [INFO ] [org.openhab.TestSwitch              ] - test_Switch changed to OFF

My questions, though I understand time has moved on from 3.4.3,…

  1. was log.info and log.error not included in 3.4.3?
  2. should I upgrade to 3.4.5, which I think was the lastest in v3?
  3. should I upgrade to 4-latest, before moving my DSL rules to ECMAscript?
  4. editing rule DSL in VScode gives me name completion of OH items; is this available for ECMAscript as well?

As usual, any hints appreciated.

  1. No and they are not included now. You can create a logger and assign it to log but the easiest thing to use is to use console. See JavaScript Scripting - Automation | openHAB and JavaScript Scripting - Automation | openHAB for details (note these are the OH 3.4 docs and should be the version you look at when working with OH 3.4). In generic JS, console is the standard way one writes to logs. Where possible, openhab-js tries to follow and use standard JS concepts and approaches over OH specific concepts and approaches. This maximizes compatibility with third party libraries and eliminates a lot of friction for users who know JS.

  2. Probably. Look at the announcement to see if the bug fixes in 3.4.5 are relevant to you.

  3. I would mainly because a lot of great features were added to the JS Scripting helper library which you might want to use from the start rather than needing to move the DSL, upgrade, then fix the code to use the latest and greatest features of JS. One important feature to watch for is the cache which I can’t remember if that existed in 3.4.

Some additional comments and suggestions:

  • TestSwitch-9dd9bcf9-9609-4c8e-b004-dc0b2b37492a: It’s so much easier to keep up with your rules and figure out where errors come with if you supply the UID to the rule rather than relying on the randomly generated UID. For one, I’m pretty sure that the UID will change every time the .js file is loaded.
rules.JSRule({
  name: "TestSwitch",
  triggers: [triggers.ItemStateChangeTrigger('test_Switch')],
  execute: function(event) {
    // the default logger name is org.openhab.automation.script.<rule UID>
    // you usually want a separate logger per rule so you need to set the 
    // logger name in the rule instead of globally to the file
    // changing the logger name is optional
    console.loggerName = "org.openhab.TestSwitch"; 
    console.info("test_Switch changed to " + event.newState);
  },
  id: "testSwitch",
  description: "A rule to test how a rule works",
  tags: ["test"]
});

Also be sure to look into rule builder which can make nice and concise rules, especially for simple rules:

rules.when().item('test_Switch').changed()
     .then( () => {
       console.info("test_switch changed to " + event.newState);
     })
     .build('TestSwitch', 'A rule to test how a rule works', ['test'], 'testSwitch');

Or because the then is a one liner:

rules.when().item('test_Switch').changed()
     .then( () =>  console.info("test_switch changed to " + event.newState) )
     .build('TestSwitch', 'A rule to test how a rule works', ['test'], 'testSwitch');

There are lot of nice shortcuts with rule builder like item('test_Switch').for(time.Duration.ofSeconds(5)) which will trigger the rule if the Item remains in the same state for five seconds.

1 Like

Well, Rich has done it again and beats the AI with his response! :100:

I am not going into the detail, as I am sure you understand it all too well.
In essence: I don’t like rule DSL, and have the idea to move one time only, to a new rule language for these reasons:

  1. most forum-supported language
  2. long-term supported language
  3. is better (as in easier for me) to learn and manage
  4. easier to relearn (I do not dabble in OH all the time, sometimes a year of more passes before I need to add something, and then it is copying what I have done before; e.g. a new SmartPlug, and often a new Thing for a micro-controller sending MQTT messages); meaning I have forgotten most of it, and turning almost 70 means brains cells seem to vanish with time too).

However, while I like the rule builder rules, I am confused. Is this another language? Can I do everything in it I can do in ECMAscript. What should I settle on?

And another one, given that DSL rules will stay, should I even bother migrating them (that is 7,800 code lines, no blanks, no comments, in 40 rules files) to ECMAscript?

As for my AI comment, I found Grok to be the best (of the AIs), and use it, instead of ‘googling’ for quick results (or non-results). :slight_smile: changing the logger to fix my first rule came from Grok; I would never figured that one.

And, is it true that basicUI is not supported in v4? I have got everything in there (basicUI = sitemap?!). Is there a migration path for the sitemap functionality?

JS provides two ways to create rules in .js files. JSRule and Rule Builder but it’s all JS.

Builder is a standard way in JS (and other languages) to simplify the creation of complex Objects (e.g. an OH Rule). Instead of creating a big data structure that gets passed to a function to create the Object:

{
  name: "TestSwitch",
  triggers: [triggers.ItemStateChangeTrigger('test_Switch')],
  execute: function(event) {
    // the default logger name is org.openhab.automation.script.<rule UID>
    // you usually want a separate logger per rule so you need to set the 
    // logger name in the rule instead of globally to the file
    // changing the logger name is optional
    console.loggerName = "org.openhab.TestSwitch"; 
    console.info("test_Switch changed to " + event.newState);
  },
  id: "testSwitch",
  description: "A rule to test how a rule works",
  tags: ["test"]
}

or creating a function with a bunch of arguments, some required, some not:

sendBroadcastNotification("Motion detected in the apartment!", "motion", "Motion Tag",
                                    "Motion Detected", "motion-id-1234", null, "https://apartment.my/camera-snapshot.jpg",
                                    "Turn on the light=command:Apartment_Light:ON", null, null)

the Builder provides a series of functions you can call to configure the object for you.

rules.when().item('test_Switch').changed()
     .then( () => {
       console.info("test_switch changed to " + event.newState);
     })
     .build('TestSwitch', 'A rule to test how a rule works', ['test'], 'testSwitch');

or

  actions.notificationBuilder('Motion detected in the apartment!')
    .withIcon('motion')
    .withTag('Motion Tag')
    .withTitle('Motion Detected')
    .withReferenceId('motion-id-1234')
    .withMediaAttachment('https://apartment.my/camera-snapshot.jpg')
    .addActionButton('Turn on the light', 'command:Apartment_Light:ON')
    .send();

The resulting code is much easier to read and understand because you don’t have to remember “what’s that third argument for again?”.

Yes, you can do everything with Rule Builder that you can do with JSRule.

You don’t have to settle on either. Both can be used ate the same time. Builder is good for rules that are simple or in cases where you need a little more complex of a trigger (e.g. the for() discussed above) but either can be used.

No, BasicUI is supported in OH 4 and OH 5 (which should be released in July) same as it always was. Sitemaps also have a bunch of new features like an arbitrary input field, button grid, etc.

1 Like