Logging & Progressbars Inside Innovator
In Aras, we have a lot of different automations running at any given time, and there is no clean way (I know of) to see them all in one place. Further, I don't have access to the server, so I can't access the log files you might typically see so I'm looking for a way to write those directly into Aras. So I set out to address these issues, by creating an ItemType called Progressbar with fields for tracking progress and on the form I created a html object and a button to trigger a 'ProgressBarClient' Method, which repeatedly calls a 'ProgressBarServer' Method multiple times. This gets me an interface to see the status of the processing... So before I spend too much time on this, I thought I'd see if the community has any ideas on better ways to approach this or ideas for improvement. Some thoughts: Using the button on the item probably wasn't the best move, it stops processing if you close the item. It also gave me a lot of headaches getting the typing right I should probably set it up so the C# method doesn't require a return to update the progress I was hoping for a live progressbar in the search grid... but using the html on the form isn't going to work because its not a property // Javascript Client Method: ProgressBarClient console.group("---Action Trigger---"); // ProgressBar ItemType Values // barmin - int - minimum value of progressbar // barmax - int - maximum value of progressbar // barvalue - int - current value of progressbar // barpercent - decimal - % complete, barvalue / barmax // currenttask - string - text description of current action // debuglog - Text - log of all activity // errorlog - Text - log of error messages console.log("Name: ProgressBar Prototype"); console.log("Description: This will call a server side method and update a progressbar"); console.log(""); console.log("Setting up progressbar..."); // --- Instantiate Innovator object at the beginning --- var inn; try { inn = new Innovator(); console.log("Innovator object instantiated successfully."); } catch (e) { console.error("CRITICAL ERROR: Failed to instantiate Innovator. The global 'Innovator' might not be a constructor or is not available. Error: " + e.message); alert("An internal error occurred. Please contact support. (Innovator instantiation failed)"); console.groupEnd(); return; } // Basic check to ensure 'inn' is a valid object after instantiation if (!inn || typeof inn.getItemById !== 'function') { console.error("CRITICAL ERROR: The 'inn' object does not have the expected Aras API methods (e.g., getItemById) even after instantiation."); alert("An internal error occurred. Please contact support. (Invalid Innovator object)"); console.groupEnd(); return; } console.log("Innovator object retrieved and validated."); // Get the current Aras Item object (assuming parent.item is available and is an XML DOM node) var currentArasItemXml = parent.item; if (!currentArasItemXml) { console.error("CRITICAL ERROR: Aras Item XML object (parent.item) could not be retrieved."); alert("An internal error occurred. Please contact support. (Aras Item XML not found)"); console.groupEnd(); return; } console.log("Current Aras Item XML object retrieved."); var itemId = currentArasItemXml.getAttribute('id'); console.log("item ID:" + itemId); var itemTypeName = currentArasItemXml.getAttribute('type'); console.log("item Type:" + itemTypeName); if (!itemId || !itemTypeName) { console.error("CRITICAL ERROR: Item ID or ItemType name not found from parent.item."); alert("An internal error occurred. Please contact support. (Item ID/Type missing)"); console.groupEnd(); return; } var currentArasItem = inn.getItemById(itemTypeName, itemId); if (currentArasItem.isError()) { console.error("CRITICAL ERROR: Failed to fetch full Aras Item object: " + currentArasItem.getErrorString()); alert("An internal error occurred. Please contact support. (Failed to fetch item)"); console.groupEnd(); return; } console.log("Full Aras Item object fetched."); // Find the form field control for our property var propName = "html0"; // This is correct based on your HTML inspection var fieldContainerEl; // This will be the <div> with name="html0" var targetHtmlEl; // This will be the inner <div class="sys_f_value"> // --- MODIFIED: Robust way to find the field element without aras.uiFindField --- console.log("Attempting to find field for property: " + propName + " directly in the DOM..."); // Try to find the element in the current document (most likely the iframe document) fieldContainerEl = document.getElementsByName(propName)[0]; // If not found, try to find it in the top document (less likely for form fields, but a fallback) if (!fieldContainerEl) { try { fieldContainerEl = top.document.getElementsByName(propName)[0]; } catch (e) { console.warn("Could not access top.document directly to find field. Error: " + e.message); } } if (!fieldContainerEl) { console.error("CRITICAL ERROR: Progress bar field '" + propName + "' not found on form. Please ensure the field's HTML 'name' attribute is '" + propName + "' and the form is loaded."); alert("Progress bar field '" + propName + "' not found on form. Please ensure the field's HTML 'name' attribute is '" + propName + "' and the form is loaded."); console.groupEnd(); return; } console.log("Progress bar field container element found. Name attribute used: " + fieldContainerEl.name); // Find the inner div with class "sys_f_value" to inject the HTML targetHtmlEl = fieldContainerEl.querySelector('.sys_f_value'); if (!targetHtmlEl) { console.error("CRITICAL ERROR: Could not find the inner '.sys_f_value' div within the '" + propName + "' field container."); alert("An internal error occurred. Please contact support. (Target HTML element for injection not found)"); console.groupEnd(); return; } console.log("Target HTML element for injection found."); // --- END MODIFIED SECTION --- // Create basic progress bar HTML function renderBar(percent, currentTask) { percent = Math.max(0, Math.min(100, percent)); return '' + '<div style="border:1px solid #999;width:100%;height:18px;background:#f5f5f5;">' + ' <div style="height:100%;width:' + percent + '%;background:#4caf50;transition:width 0.2s;"></div>' + '</div>' + '<div style="font-size:10px;margin-top:2px;">' + percent.toFixed(2) + '% ' + (currentTask ? '(' + currentTask + ')' : '') + '</div>'; } // Helper to set the field value + UI function setProgress(barvalue, barmax, currenttask) { var percent = (barmax > 0) ? (barvalue / barmax) * 100 : 0; var renderedHtml = renderBar(percent, currenttask); // Update rendered field in current UI targetHtmlEl.innerHTML = renderedHtml; } // Initialize at 0% setProgress(0, 100, "Initializing..."); console.log("Progress bar initialized to 0%."); // --- START: Replaced simulated work with Server Method call --- // Variables to maintain state across server calls var progressItemId = null; // Stores the ID of the Progressbar item created/updated by the server var currentStep = 0; // Stores the current step to send to the server // Function to call the server method and update progress function callServerMethodForProgress(initialCall) { console.log("Calling the server-side method 'ProgressBarServer'..."); var serverMethod = inn.newItem("Method", "ProgressBarServer"); serverMethod.setProperty("itemId", itemId); // Pass the ID of the item that triggered this client method serverMethod.setProperty("itemTypeName", itemTypeName); // Pass the ItemType name serverMethod.setProperty("initialCall", initialCall ? "true" : "false"); serverMethod.setProperty("progressItemId", progressItemId || ""); // Pass the ID of the Progressbar item serverMethod.setProperty("currentStep", currentStep.toString()); // Pass the current step var result = serverMethod.apply(); if (result.isError()) { var errorMsg = result.getErrorString(); console.error("Server method 'ProgressBarServer' error: " + errorMsg); alert("Server method error: " + errorMsg); setProgress(0, 100, "Error: " + errorMsg); // Show error in progress bar console.groupEnd(); return; // Stop progress } // Parse the response from the server method var progressItem = result.getItemByIndex(0); if (!progressItem || progressItem.isError()) { console.error("Server method 'ProgressBarServer' returned an invalid or empty item."); alert("Server method returned invalid data."); setProgress(0, 100, "Error: Invalid server response"); console.groupEnd(); return; } // Extract properties from the server's response progressItemId = progressItem.getProperty("id", progressItemId); // Update progressItemId if it's the first call var barmin = parseInt(progressItem.getProperty("barmin", "0")); var barmax = parseInt(progressItem.getProperty("barmax", "100")); var barvalue = parseInt(progressItem.getProperty("barvalue", "0")); var barpercent = parseFloat(progressItem.getProperty("barpercent", "0")); var currenttask = progressItem.getProperty("currenttask", "Processing..."); var debuglog = progressItem.getProperty("debuglog", ""); var errorlog = progressItem.getProperty("errorlog", ""); var isComplete = progressItem.getProperty("is_complete", "false") === "true"; currentStep = parseInt(progressItem.getProperty("next_step", (currentStep + 1).toString())); // Get next step from server console.log("Server response - Value: " + barvalue + ", Max: " + barmax + ", Task: " + currenttask + ", Complete: " + isComplete + ", Progress Item ID: " + progressItemId); if (debuglog) console.log("Server Debug Log:\n" + debuglog); if (errorlog) console.error("Server Error Log:\n" + errorlog); // Update the client-side progress bar setProgress(barvalue, barmax, currenttask); // Persist the progress to the Progressbar ItemType (if it's the item being edited) // This assumes the form you are on *is* the Progressbar ItemType form. // If this client method is on a *different* ItemType's form, you would need to // load the Progressbar item by its ID and update its properties. if (itemTypeName === "Progressbar" && itemId === progressItemId) { currentArasItem.setProperty("barmin", barmin.toString()); currentArasItem.setProperty("barmax", barmax.toString()); currentArasItem.setProperty("barvalue", barvalue.toString()); currentArasItem.setProperty("barpercent", barpercent.toFixed(2)); currentArasItem.setProperty("currenttask", currenttask); currentArasItem.setProperty("debuglog", debuglog); currentArasItem.setProperty("errorlog", errorlog); // Note: This only updates the client-side item object. // To save to the database, the user would need to click 'Save' on the form, // or you would need to explicitly call currentArasItem.apply("update") here, // which might be too frequent and cause performance issues. // The server method already saves the Progressbar item. } else { // If the client method is on a different ItemType, we need to explicitly update the Progressbar item // This is an example of how to update the Progressbar item from a different context var updateProgressItem = inn.newItem("Progressbar", "edit"); updateProgressItem.setID(progressItemId); updateProgressItem.setProperty("barmin", barmin.toString()); updateProgressItem.setProperty("barmax", barmax.toString()); updateProgressItem.setProperty("barvalue", barvalue.toString()); updateProgressItem.setProperty("barpercent", barpercent.toFixed(2)); updateProgressItem.setProperty("currenttask", currenttask); updateProgressItem.setProperty("debuglog", debuglog); updateProgressItem.setProperty("errorlog", errorlog); // updateProgressItem.apply("update"); // Uncomment if you want to save from client, but server already does this. } if (!isComplete) { // If not complete, call the server method again after a short delay setTimeout(function () { callServerMethodForProgress(false); }, 500); // Adjust delay as needed } else { inn.showStatusMessage("Processing completed.", "info"); console.log("Processing completed."); console.groupEnd(); } } // Start the process by making the initial call to the server method callServerMethodForProgress(true); // --- END: Replaced simulated work --- console.log("Notify user of completion (handled by showStatusMessage)"); // C# Server Method: ProgressBarServer Innovator innovator = this.getInnovator(); // Get parameters from the client string progressItemId = this.getProperty("itemId"); string currentStepStr = this.getProperty("currentStep", "0"); int currentStep = int.Parse(currentStepStr); Item progressBarItem; int barmin = 0; int barmax = 10; // Define total steps for the simulated task // --- Step 1: Load the existing Progressbar Item --- if (string.IsNullOrEmpty(progressItemId)) { return innovator.newError("Error: 'progressItemId' is required but was not provided. The Progressbar item must exist."); } // Load the item first to get its current state progressBarItem = innovator.newItem("Progressbar", "get"); progressBarItem.setID(progressItemId); progressBarItem = progressBarItem.apply(); if (progressBarItem.isError()) { return innovator.newError("Failed to load Progressbar item (ID: " + progressItemId + "): " + progressBarItem.getErrorString()); } // Ensure barmax is consistent if it was set on creation barmax = int.Parse(progressBarItem.getProperty("barmax", barmax.ToString())); // --- Step 2: Attempt to lock the item for update --- // Use the loaded item to attempt the lock. // This is important: applying 'lock' on the loaded item ensures we're working with the same item instance. Item lockResult = progressBarItem.apply("lock"); if (lockResult.isError()) { string errorString = lockResult.getErrorString(); // Check for the specific Aras error message indicating it's already locked by *this* user. // The exact message for "already locked by current user" can vary slightly. // We'll use a broader check for "locked" and then check the user. if (errorString.Contains("Item is already locked") || errorString.Contains("Aras.Server.Core.ItemIsAlreadyLockedException")) { // Now, check *who* locked it. // Get the locked_by property from the item (if it exists and is populated) string lockedById = progressBarItem.getProperty("locked_by_id", ""); string currentUserId = innovator.getUserID(); if (lockedById == currentUserId) { // Item is already locked by the current user. This is expected in polling. // Proceed with the update. No error needed. System.Diagnostics.Trace.WriteLine("DEBUG: Progressbar item (ID: " + progressItemId + ") already locked by current user. Proceeding."); } else if (!string.IsNullOrEmpty(lockedById)) { // Item is locked by another user. This is a genuine conflict. Item lockedByUser = innovator.getItemById("User", lockedById); string lockedByUserName = lockedByUser.getProperty("keyed_name", "Unknown User"); return innovator.newError("Failed to lock Progressbar item (ID: " + progressItemId + "): Item is already locked by another user: " + lockedByUserName + "."); } else { // Item is locked, but locked_by_id is not set or couldn't be determined. // This might indicate a stale lock or an unexpected state. return innovator.newError("Failed to lock Progressbar item (ID: " + progressItemId + "): Item is already locked, but the locker could not be identified. Error: " + errorString); } } else { // Some other locking error occurred. return innovator.newError("Failed to lock Progressbar item (ID: " + progressItemId + "): " + errorString); } } // If lockResult is not an error, or if it was already locked by current user, // the progressBarItem is now locked by this session and ready for update. // --- Step 3: Simulate work and update progress --- string currentTaskDescription = ""; bool isComplete = false; // Simulate different steps switch (currentStep) { case 0: currentTaskDescription = "Step 1: Preparing data..."; System.Threading.Thread.Sleep(500); // Simulate work break; case 1: currentTaskDescription = "Step 2: Processing records..."; System.Threading.Thread.Sleep(700); // Simulate work break; case 2: currentTaskDescription = "Step 3: Validating inputs..."; System.Threading.Thread.Sleep(400); // Simulate work break; case 3: currentTaskDescription = "Step 4: Performing calculations..."; System.Threading.Thread.Sleep(800); // Simulate work break; case 4: currentTaskDescription = "Step 5: Generating reports..."; System.Threading.Thread.Sleep(600); // Simulate work break; case 5: currentTaskDescription = "Step 6: Saving results..."; System.Threading.Thread.Sleep(500); // Simulate work break; case 6: currentTaskDescription = "Step 7: Finalizing process..."; System.Threading.Thread.Sleep(300); // Simulate work break; case 7: currentTaskDescription = "Step 8: Cleaning up temporary files..."; System.Threading.Thread.Sleep(200); // Simulate work break; case 8: currentTaskDescription = "Step 9: Notifying stakeholders..."; System.Threading.Thread.Sleep(100); // Simulate work break; case 9: currentTaskDescription = "Step 10: Task completed."; isComplete = true; break; default: currentTaskDescription = "Unknown step or task completed."; isComplete = true; break; } // Update properties on the progressBarItem object int barvalue = currentStep + 1; // Increment value for the next step if (isComplete) barvalue = barmax; // Ensure it reaches max when complete double barpercent = (double)barvalue / barmax * 100; progressBarItem.setProperty("barvalue", barvalue.ToString()); progressBarItem.setProperty("barpercent", barpercent.ToString("F2")); // Format to 2 decimal places progressBarItem.setProperty("currenttask", currentTaskDescription); progressBarItem.setProperty("debuglog", progressBarItem.getProperty("debuglog", "") + currentTaskDescription + " at " + DateTime.Now.ToString("HH:mm:ss") + "\n"); // --- Step 4: Persist the updated Progressbar item --- // At this point, progressBarItem *must* be locked by this session. Item updateResult = progressBarItem.apply("update"); if (updateResult.isError()) { // If update fails here, it's a critical error. // This could still be a locking issue if the lock was somehow lost or not acquired correctly. return innovator.newError("Failed to update Progressbar item (ID: " + progressItemId + "): " + updateResult.getErrorString()); } // --- Step 5: Prepare the response for the client --- Item resultItem = innovator.newItem("Progressbar", "get"); // Return the updated Progressbar item resultItem.setProperty("id", progressItemId); // Ensure the ID is returned resultItem.setProperty("barmin", progressBarItem.getProperty("barmin")); resultItem.setProperty("barmax", progressBarItem.getProperty("barmax")); resultItem.setProperty("barvalue", progressBarItem.getProperty("barvalue")); resultItem.setProperty("barpercent", progressBarItem.getProperty("barpercent")); resultItem.setProperty("currenttask", progressBarItem.getProperty("currenttask")); resultItem.setProperty("debuglog", progressBarItem.getProperty("debuglog")); resultItem.setProperty("errorlog", progressBarItem.getProperty("errorlog")); resultItem.setProperty("is_complete", isComplete ? "true" : "false"); resultItem.setProperty("next_step", (currentStep + 1).ToString()); // Tell client the next step to send // --- Step 6: Unlock the item if the task is complete --- Item unlockResult = progressBarItem.apply("unlock"); // Unlock the item that was locked if (unlockResult.isError()) { // Log the unlock error, but don't fail the entire method as the task is complete. System.Diagnostics.Trace.WriteLine("ERROR: Failed to unlock Progressbar item (ID: " + progressItemId + "): " + unlockResult.getErrorString()); } return resultItem;206Views0likes4CommentsCreate log file with CCO.Logger
Hello, I want to start using the CCO.Logger for creating log files and I didn't find any information about this. The only thing I found is a topic that the appsettings needs to be configured. I'm assuming that over there I can manage the writing level and also where the log file will be created. Currently my appsettings is looking like this: { "LoggerConfiguration": { "MinimumLevel": "Fatal", "WriteTo": [ { "Name": "Console" }, { "Name": "File" }, { "Name": "EventLog" } ] } } Is someone can point me to relevant documentation, or give me some examples, I will appreciate it. Thanks in advanceSolved257Views0likes6CommentsCreate Server Logs
Dear Aras Community, I have the following problem. I want to activate the server logs. To do this, I set <operating_parameter key = "debug_log_flag" value = "true" /> as described in the instructions and restarted the server. The following error occurs when I want to log in ( Could not find a part of the path 'D:\ARASInstances\KPEC\Innovator\Server\logs\2021-12-23-11-04-59-26.xml'). Can you please help me? Best regards1.9KViews1like2CommentsIs it possible to use the System Event Log for logging custom events?
Hi community, the system event log by default is configured so we can track successful and failed login attempts. I wonder if we can also use the log to track other events of interest? E.g. I have a few C# Methods where I want to track when and how often the Methods fails. I have added a custom SystemEvent type for "MethodFailed". But when I use the following code, I will get the following error message: "Requested value "MethodFailed" was not found. CCO.SystemEventLogger.CreateSystemLogRecord("MethodFailed","Method_doSomething",inn.getUserID(),"Method xy failed, value z invalid" ); This line of code in a C# Method only works, when I use one of the standard OOTB system events: CCO.SystemEventLogger.CreateSystemLogRecord("SuccessfulLogin","Method_doSomething",inn.getUserID(),"Method xy failed, value z invalid" ); Is it possible to use own system events types at all? If yes, where do I have to add my custom event type? Or maybe someone know some other log options? I don´t want to build a separate ItemType for this use case. The System Event log would fit very well, cause it is checked and cleared on a regular basis anyway. Would be cool if somebody has an idea how to solve this one!Solved10KViews0likes5CommentsHow to Track the deleted Item
Hi Experts, In my system, I have created one ItemType called "PDT Projects". So, whatever the Items will get created and deleted under this "PDT Projects", I want to track those items. Is it possible to get deleted Items Log anywhere in the system? So I can show a report to end users these are the deleted Items by this user. Regards, Maddy.3.3KViews0likes1Comment