XPages Custom Error Page - get error message and line - xpages

I'm trying to generate a custom error page for my xpages. I googled a lot of solutions and so far I get an error page telling me, that an error occured.
But I can't get the information what exactly happened (in this case the error is, that an "doc" has to be saved, but i named the variable "docs" just to get error).
All I do is:
var errObj = requestScope.error;
output = errObj.getCause().getErrorPropertyId();
output = errObj.getCause().getComponentId();
As soon as I try to call getExpressionText() I get an error 500.
How do I get the information, where the error happened (line number) and the variable that caused the error? - just like I do using the standard error page.

The error line and details are not easily accessible from requestScope.error. If you look at the source code for the latest release of Mark Leusink's Debug Toolbar, you'll see he's parsing the stack trace to get the details.
However, you can access all the relevant information using the underlying Java class for the SSJS exception - com.ibm.jscript.InterpretException using getErrorLine(). The getLocalizedMessage() method gets the error detail that usually starts "Script interpreter error". The getExpressionText() method retrieves the line that threw the error.
If you take a look at the XPages OpenLog Logger project I put on OpenNTF, that's what I use to log full details to OpenLog. http://www.openntf.org/Internal/home.nsf/project.xsp?action=openDocument&name=XPages%20OpenLog%20Logger
You can see the source code of the OpenLogPhaseListener which uses those methods here: https://github.com/paulswithers/openlogjava/blob/master/OpenLogJava/WebContent/WEB-INF/src/com/paulwithers/openLog/OpenLogPhaseListener.java
Even if you're not a Java expert, from use of SSJS the key parts should be understandable. Line 84 captures uncaught exceptions - when XPages routes to the default error page. That uses the methods I mentioned.
Lines 98 to 105 are the ones that log out all the details if you just use a catch block, passing OpenLogBean.addError(e, this) where e is the error object and this is the component the error occurs on. error.getError() in the Java code retrieves that error object. To get the typeahead in SSJS you'll need to use catch(e:com.ibm.jscript.InterpretException) I believe.
I haven't tested this, I've just working back from what I used for the project on OpenNTF.

Have a look at this XSnippet by Tony McGuckin: http://openntf.org/XSnippets.nsf/snippet.xsp?id=custom-error-page-cw-cause-and-stacktrace-information. It uses the following to output details on the error:
var output = requestScope.error.toString()+"\n\n";
if(requestScope.error instanceof com.ibm.xsp.exception.XSPExceptionInfo){
var codeSnippet = requestScope.error.getErrorText();
var control = requestScope.error.getErrorComponentId();
var cause = requestScope.error.getCause();
output += "In the control : " + control + "\n\n";
if(cause instanceof com.ibm.jscript.InterpretException){
var errorLine = cause.getErrorLine();
var errorColumn = cause.getErrorCol();
output += "At line " + errorLine;
output += ", column " + errorColumn + " of:\n";
}else{
output += "In the script:\n";
}
output += codeSnippet;
}
return output;

For now I've dealt with this problem by using the Debug Toolbar and the OpenLog Database.
If an error occurs the user only gets a custom error page (using the example of the Debug Toolbar) with the information, that something went wrong. So he does not have to bother with any other problems or even the Stack Trace. But at the same moment he gets the error page, the error is logged in our Log-Database with all informations needed (like line, exact error message etc.).
I've also implemented an "Report this problem" Link-Button to create a new E-Mail containing important information about the session the user is currently in.

Related

Google Stackdriver Error Reporting API - Include Custom Data

I need to include a custom data object/JSON string with an error report, without losing the stacktrace that Stackdriver seems to capture. Setting a JSON string as the message doesn't seem like an ideal solution.
I have seen references to a jsonPayload key online, but haven't had success setting it in the report.
In the Node.js systems I am integrating Stackdriver into (via logging client), I have a logger function that accepts additional data about the environment, the error stack and any supporting data that led to the error, and I wish to include this with the report so that they can be quickly investigated.
I have instead had to use the Google Stackdriver Logging API to handle this in the interim, but I find the metrics viewer a little convoluted and it's also hard to keep track of which logs have been dealt with.
I saw a stale question on this previously, but didn't want to hijack it. Nor did it have any solution.
Hoping there's a solution!
What I do is storing custom payload to Datastore and put the link to Datastore viewer to the error exception message. Here is for example how it looks in Ruby (the method stores url and html strings that I need for debug as attributes of the Datastore entity of kind exception_details):
def report_error url, html
begin
raise "https://console.cloud.google.com/datastore/entities/query/gql?gql=#{
CGI.escape "SELECT * FROM exception_details WHERE __key__=Key(exception_details, #{
Datastore.save( Datastore.entity("exception_details") do |error|
error["url"] = url
error["html"] = html
error.exclude_from_indexes! "html", true
end ).first.key.id
})"
}"
rescue => e
Google::Cloud::ErrorReporting.report e
end
end
Here is an email I get:
Instead of clicking the blue button I visit the hyperlink where I can now inspect the html variable that I stored:

Swift UIWebView Optional is nil

In the below example, infoScroller is a UIWebView and println(HTMLDescription) prints a lovely string of HTML. However, the attempt to loadHTMLString gets the runtime error: fatal error: Can't unwrap Optional.None
if let HTMLDescription = self.myData?.content? {
println(HTMLDescription)
infoScroller.loadHTMLString(HTMLDescription, baseURL: nil)
}
I've tried every combination of ! and ? in both the assignment and use of the string but I get this same error every time, though the variable never fails to print out perfectly to the console.
There is another value that I set using the same method and it works fine. Both are strings, but the other one is more simple in that HTMLDescription is multiline and the working one is not.
Edit: The discussion in the comments prompted me to check the infoScroller and it's description as printed in the console is: (#sil_weak UIWebView!) infoScroller =
I'm thinking that's the issue, but I'm not sure what it means or how to fix it.
Edit 2: This has to be the issue. println(infoScroller.description) yields the exact same error.
Got it! This question put me on the path. I was trying to load the content before the view was fully loaded. Moved loadHTMLString() into viewDidLoad(). Stupid simple.

Disable SqlError Reporting on 500 Error for a Production App

When there is a 500 error due to a bad query, Yesod displays the sql query on the page or parts of it. While this has been very helpful in development, I would like to avoid it in production. Is there a way to just display Internal Server Error and no other details?
Chances of having an error with sql is very low but it can still happen if a rogue user tries to come up with a badly formed input (the error message below is just a contrived example to illustrate the issue). There are DB constraints in place to avoid storing these bad inputs but the error message seems to be giving clues about the query, which I would like to avoid (I have seen larger parts of the query in some instances unlike the one below).
SqlError {sqlState = "42703", sqlExecStatus = FatalError,
sqlErrorMsg = "... duplicate key value (1480, 9, 3) violates unique constraint ...",
sqlErrorDetail = "", sqlErrorHint = ""}
I would like to avoid throwing a lot of try/catch-type blocks just to prevent these edge cases. A simple solution would be not display the sqlerror messages. Any thoughts on how I could do it? I am using Keter to deploy my app.
# Keter.yaml
exec: ../dist/build/MyApp/MyApp
args:
- production
host: www.example.com
Update: Here is a simple errorHandler to address this issue per Michael's answer.
-- Matching only InternalError. All other errors are
-- sent to the defaultErrorHandler as-is
newErrorHandler (InternalError msg) = do
$(logWarn) (append "Error Response: " $ pack (show (InternalError msg)))
defaultErrorHandler (InternalError "Custom message here")
newErrorHandler (errMsg) = defaultErrorHandler errMsg
Thanks!
You can override the default error handler in your Yesod typeclass using errorHandler. The default value is defaultErrorHandler, which displays the full value of the exception thrown. You can instead replace that with whatever information you want, conditional on the development value provided by the scaffolded site.
If you wanted to get a bit fancier, I'd recommend logging each exception thrown into your database and generating a random token, then displaying that token to the user on the error handler page. That way, the user can provide you that information so that you can better debug your application.
Using newErrorHandler did not work for me, so I instead solved it this way:
-- Foundation.hs
instance Yesod App where
errorHandler (InternalError e) = do
$(logWarn) e
fmap toTypedContent $ defaultLayout $ do
setTitle "Example site"
$(widgetFile "error")
errorHandler other = defaultErrorHandler other
And then create a custom error template in templates/error.hamlet…
$newline never
<h1>Internal Error
<p>Sorry, something has gone wrong.
You can add more error handers, pattern matching on the type of error. If you remove the other pattern, the compiler should tell you your pattern match is inexhaustive and provide some more patterns to match against.

Java exception: "Can't get a Writer while an OutputStream is already in use" when running xAgent

I am trying to implement Paul Calhoun's Apache FOP solution for creating PDF's from Xpages (from Notes In 9 #102). I am getting the following java exception when trying to run the xAgent that does the processing --> Can't get a Writer while an OutputStream is already in use
The only changes that I have done from Paul's code was to change the package name. I have isolated when the exception happens to the SSJS line: var jce: DominoXMLFO2PDF = new DominoXMLFO2PDF(); All that line does is instantiate the class, there is no custom constructor. I don't believe it is the code itself, but some configuration issue. The SSJS code is in the beforeRenderResponse event where it should be, I haven't changed anything on the xAgent.
I have copied the jar files from Paul's sample database to mine, I have verified that the build paths are the same between the two databases. Everything compiles fine (after I did all this.) This exception appears to be an xpages only exception.
Here's what's really going on with this error:
XPages are essentially servlets... everything that happens in an XPage is just layers on top of a servlet engine. There are basically two types of data that a servlet can send back to whatever is initiating the connection (e.g. a browser): text and binary.
An ordinary XPage sends text -- specifically, HTML. Some xAgents also send text, such as JSON or XML. In any of these scenarios, however, Domino uses a Java Writer to send the response content, because Writers are optimized for sending Character data.
When we need to send binary content, we use an OutputStream instead, because streams are optimized for sending generic byte data. So if we're sending PDF, DOC/XLS/PPT, images, etc., we need to use a stream, because we're sending binary data, not text.
The catch (as you'll soon see, that's a pun) is that we can only use one per response.
Once any HTTP client is told what the content type of a response is, it makes assumptions about how to process that content. So if you tell it to expect application/pdf, it's expecting to only receive binary data. Conversely, if you tell it to expect application/json, it's expecting to only receive character data. If the response includes any data that doesn't match the promised content type, that nearly always invalidates the entire response.
So Domino in its infinite wisdom protects us from making this mistake by only allowing us to send one or the other in a single request, and throws an exception if we disobey that rule.
Unfortunately... if there's any exception in our code when we're trying to send binary content, Domino wants to report that to the consumer... which tries to invoke the output writer to send HTML reporting that something went wrong. Except we already got a handle on the output stream, so Domino isn't allowed to get a handle on the output writer, because that would violate its own rule against only using one per response. This, in turn, throws the exception you reported, masking the exception that actually caused the problem (in your case, probably a ClassNotFoundException).
So how do we make sure that we see the real problem, and not this misdirection? We try:
try {
/*
* Move all your existing code here...
*/
} catch (e) {
print("Error generating dynamic PDF: " + e.toString());
} finally {
facesContext.responseComplete();
}
There are two reasons this is a preferred approach:
If something goes wrong with our code, we don't let Domino throw an exception about it. Instead, we log it (instead of using print to send it to the console and log, you could also toss it to OpenLog, or whatever your preferred logging mechanism happens to be). This means that Domino doesn't try to report the error to the user, because we've promised that we already reported it to ourselves.
By moving the crucial facesContext.responseComplete() call (which is what ultimately tells Domino not to send any content of its own) to the finally block, this ensures it will get executed. If we left it inside the try block, it would get skipped if an exception occurs, because we'd skip straight to the catch... so even though Domino isn't reporting our exception because we caught it, it still tries to invoke the response writer because we didn't tell it not to.
If you follow the above pattern, and something's wrong with your code, then the browser will receive an incomplete or corrupt file, but the log will tell you what went wrong, rather than reporting an error that has nothing to do with the root cause of the problem.
I almost deleted this question, but decided to answer it myself since there is very little out on google when you search for the exception.
The issue was in the xAgent, there is a line importPackage that was incorrect. Fixing this made everything work. The exception verbage: "Can't get a Writer while an OutputStream is already in use" is quite misleading. I don't know what else triggers this exception, but an alternative description would be "Java class ??yourClass?? not found"
If you found this question, then you likely have the same issue. I would ignore what the exception actually says, and check your package statements throughout your application. The java code will error on its own, but your SSJS that references the java will not error until runtime, focus on that code.
Update the response header after the body can solve this kind of problem, example :
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.getWriter().write("<html><body>...</body></html>");
response.setContentType("text/html");
response.setHeader("Cache-Control", "no-cache");
response.setCharacterEncoding("UTF-8");

FormatMessage gets Error 317 while trying to read EventLog

I had nearly similar problem with this.
FormatMessage Fails with error code 317
The difference is it is said as an answer that this is caused by "FORMAT_MESSAGE_FROM_SYSTEM" but when I remove it it happens again.
I am trying to read from EventLog in Windows Server 2003. But when I try to use FormatMessage function I get 317 error.
Interestingly same code works for Windows Server 2008. How can I fix this or what can I use instead of FormatMessage?
My code:
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
g_hResources, // handles DLL containing message table
MessageId,
0, // Default language
(LPWSTR) &pMessage,
0,
(va_list*)pArgs )
Good day to you..
Error 317 is "The system cannot find message text for message number 0x%1 in the message file for %2.". That means the MessageId is not an error number known to the system.
You are combining FORMAT_MESSAGE_FROM_HMODULE and FORMAT_MESSAGE_FROM_SYSTEM, which doesn't make sense. Where do you want to get the message from? Do you want to get it from g_hResources or from the system error message table? From the comment, it sounds like you want to get it from g_hResources, in which case you should remove FORMAT_MESSAGE_FROM_SYSTEM. If you still get error 317, then it means that the message number you passed doesn't exist in g_hResources.

Resources