diff --git a/src/main/java/ortus/boxlang/runtime/components/system/Log.java b/src/main/java/ortus/boxlang/runtime/components/system/Log.java
new file mode 100644
index 000000000..538506426
--- /dev/null
+++ b/src/main/java/ortus/boxlang/runtime/components/system/Log.java
@@ -0,0 +1,64 @@
+
+/**
+ * [BoxLang]
+ *
+ * Copyright [2023] [Ortus Solutions, Corp]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ortus.boxlang.runtime.components.system;
+
+import ortus.boxlang.runtime.components.Attribute;
+import ortus.boxlang.runtime.components.Component;
+import ortus.boxlang.runtime.context.IBoxContext;
+import ortus.boxlang.runtime.events.BoxEvent;
+import ortus.boxlang.runtime.scopes.Key;
+import ortus.boxlang.runtime.types.IStruct;
+
+public class Log extends Component {
+
+ private final Key writeLogKey = Key.of( "writeLog" );
+
+ /**
+ * Constructor
+ */
+ public Log() {
+ super();
+ // Uncomment and define declare argument to this Component
+ declaredAttributes = new Attribute[] {
+ new Attribute( Key.text, "string" ),
+ new Attribute( Key.file, "string" ),
+ new Attribute( Key.log, "string", "Application" ),
+ new Attribute( Key.type, "string", "Information" ),
+ };
+ }
+
+ /**
+ * Describe what the invocation of your component does
+ *
+ * @param context The context in which the Component is being invoked
+ * @param attributes The attributes to the Component
+ * @param body The body of the Component
+ * @param executionState The execution state of the Component
+ *
+ * @attribute.foo Describe any expected arguments
+ */
+ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBody body, IStruct executionState ) {
+
+ interceptorService.announce( BoxEvent.LOG_MESSAGE, attributes );
+
+ return DEFAULT_RETURN;
+ }
+
+}
diff --git a/src/test/java/ortus/boxlang/runtime/components/system/LogTest.java b/src/test/java/ortus/boxlang/runtime/components/system/LogTest.java
new file mode 100644
index 000000000..2fd2ae9b3
--- /dev/null
+++ b/src/test/java/ortus/boxlang/runtime/components/system/LogTest.java
@@ -0,0 +1,155 @@
+
+/**
+ * [BoxLang]
+ *
+ * Copyright [2023] [Ortus Solutions, Corp]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ortus.boxlang.runtime.components.system;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Paths;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import ortus.boxlang.compiler.parser.BoxSourceType;
+import ortus.boxlang.runtime.BoxRuntime;
+import ortus.boxlang.runtime.context.IBoxContext;
+import ortus.boxlang.runtime.context.ScriptingRequestBoxContext;
+import ortus.boxlang.runtime.dynamic.casters.StringCaster;
+import ortus.boxlang.runtime.scopes.IScope;
+import ortus.boxlang.runtime.scopes.Key;
+import ortus.boxlang.runtime.scopes.VariablesScope;
+import ortus.boxlang.runtime.util.FileSystemUtil;
+
+public class LogTest {
+
+ static BoxRuntime instance;
+ static String logsDirectory;
+ IBoxContext context;
+ IScope variables;
+ static Key result = new Key( "result" );
+
+ @BeforeAll
+ public static void setUp() {
+ instance = BoxRuntime.getInstance( true );
+ logsDirectory = instance.getConfiguration().runtime.logsDirectory;
+
+ }
+
+ @AfterAll
+ public static void teardown() {
+ }
+
+ @BeforeEach
+ public void setupEach() {
+ context = new ScriptingRequestBoxContext( instance.getRuntimeContext() );
+ variables = context.getScopeNearby( VariablesScope.name );
+ }
+
+ @DisplayName( "It tests the BIF Log with Script parsing" )
+ @Test
+ public void testComponentScript() {
+ instance.executeSource(
+ """
+ log text="Hello Logger!" log="Foo" file="foo.log";
+ """,
+ context, BoxSourceType.BOXSCRIPT );
+ }
+
+ @DisplayName( "It tests the BIF Log with CFML parsing" )
+ @Test
+ public void testComponentCF() {
+ instance.executeSource(
+ """
+
+ """,
+ context, BoxSourceType.CFTEMPLATE );
+ }
+
+ @DisplayName( "It tests the BIF Log with BoxLang parsing" )
+ @Test
+ public void testComponentBX() {
+ instance.executeSource(
+ """
+
+ """,
+ context, BoxSourceType.BOXTEMPLATE );
+ }
+
+ @DisplayName( "It tests the BIF Log with Script parsing" )
+ @Test
+ public void testComponentCustomLogScript() {
+ String logFilePath = Paths.get( logsDirectory, "/foo.log" ).normalize().toString();
+ if ( FileSystemUtil.exists( logFilePath ) ) {
+ FileSystemUtil.deleteFile( logFilePath );
+ }
+
+ instance.executeSource(
+ """
+ log text="Hello Logger!" log="Foo" file="foo.log";
+ """,
+ context, BoxSourceType.BOXSCRIPT );
+
+ assertTrue( FileSystemUtil.exists( logFilePath ) );
+ String fileContent = StringCaster.cast( FileSystemUtil.read( logFilePath ) );
+ assertTrue( StringUtils.contains( fileContent, "Hello Logger!" ) );
+ }
+
+ @DisplayName( "It tests the BIF Log with CF tag parsing" )
+ @Test
+ public void testComponentCustomLogCF() {
+ String logFilePath = Paths.get( logsDirectory, "/foo.log" ).normalize().toString();
+ if ( FileSystemUtil.exists( logFilePath ) ) {
+ FileSystemUtil.deleteFile( logFilePath );
+ }
+
+ instance.executeSource(
+ """
+
+ """,
+ context, BoxSourceType.CFTEMPLATE );
+
+ assertTrue( FileSystemUtil.exists( logFilePath ) );
+ String fileContent = StringCaster.cast( FileSystemUtil.read( logFilePath ) );
+ assertTrue( StringUtils.contains( fileContent, "Hello Logger!" ) );
+ }
+
+ @DisplayName( "It tests the BIF Log with BX tag parsing" )
+ @Test
+ public void testComponentCustomLogBX() {
+ String logFilePath = Paths.get( logsDirectory, "/foo.log" ).normalize().toString();
+ if ( FileSystemUtil.exists( logFilePath ) ) {
+ FileSystemUtil.deleteFile( logFilePath );
+ }
+
+ instance.executeSource(
+ """
+
+ """,
+ context, BoxSourceType.BOXTEMPLATE );
+
+ assertTrue( FileSystemUtil.exists( logFilePath ) );
+ String fileContent = StringCaster.cast( FileSystemUtil.read( logFilePath ) );
+ assertTrue( StringUtils.contains( fileContent, "Hello Logger!" ) );
+ }
+
+}
diff --git a/workbench/tasks/ScaffoldComponent.cfc b/workbench/tasks/ScaffoldComponent.cfc
index 8305f7f7a..50fc4cbff 100644
--- a/workbench/tasks/ScaffoldComponent.cfc
+++ b/workbench/tasks/ScaffoldComponent.cfc
@@ -149,6 +149,7 @@ component {
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+ import ortus.boxlang.compiler.parser.BoxSourceType;
import ortus.boxlang.runtime.BoxRuntime;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.context.ScriptingRequestBoxContext;
@@ -193,7 +194,7 @@ component {
@DisplayName( "It tests the BIF #componentName# with BoxLang parsing" )
@Test
- public void testComponentCF() {
+ public void testComponentBX() {
instance.executeSource(
"""