HylaFAX The world's most advanced open source fax server

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [hylafax-users] Reporting Needs



David,

Attached is a patch which does two things.

1) it adds SUBMIT records to xferfaxlog. This will permit you to take a look at the SUBMIT record and the corresponding SEND record(s) (and perhaps a corresponding UNSENT record) to determine how long that job was in the queue. Compare the date values (the first column in each record).

2) it adds a new "FaxAccounting" feature. Create an executable (i.e. shell script with the execute bit set) called /var/spool/hylafax/etc/FaxAccounting. Any time records are written to xferfaxlog this executable will be run with the record column values as command-line parameters. This will permit you to easily attach an external database (or whatever) to HylaFAX for accounting purposes (in other words, no need to parse xferfaxlog). Then you can easily perform whatever statistical analysis or accounting measures that you need from there.

Hope this helps,

Thanks,

Lee.


Lee Howard wrote:


David Guthu wrote:

Does anyone know of any reporting available with historical information about how many items are queued to be sent, and what the status of available resources are, etc etc? I need some really detailed information about queued time to insure that a service level defined is being met by the resources allocated. Any help is greatly appreciated. Does IFAX Enterprise verison offer this?



Although such a feature could be added to HylaFAX, I would think that it should be a fairly easy task to run a cron job parsing faxstat output to come up with a relatively good view of the queue size over time and the modem status.


One thing that I do think could be added to HylaFAX that would make a significant improvement with respect to queue management would be to add "SUBMIT" records to xferfaxlog. That way xferfaxstats or something could parse the average time-in-queue for each destination or whatever. If that average gets too high, then it's time to add modems/lines.

Lee.

____________________ HylaFAX(tm) Users Mailing List _______________________
To subscribe/unsubscribe, click http://lists.hylafax.org/cgi-bin/lsg2.cgi
On UNIX: mail -s unsubscribe hylafax-users-request@xxxxxxxxxxx < /dev/null
*To learn about expensive HylaFAX(tm) support, mail sales@xxxxxxxxx*



diff -Nru hylafax.orig/faxd/FaxAcctInfo.c++ hylafax/faxd/FaxAcctInfo.c++
--- hylafax.orig/faxd/FaxAcctInfo.c++	2005-11-14 08:33:31.000000000 -0800
+++ hylafax/faxd/FaxAcctInfo.c++	2005-11-15 15:24:36.518816560 -0800
@@ -40,28 +40,43 @@
 {
     bool ok = false;
     int fd = Sys::open(FAX_XFERLOG, O_RDWR|O_CREAT|O_APPEND, 0644);
+
+    char timebuf[80];
+    strftime(timebuf, sizeof (timebuf), "%D %H:%M", localtime(&start));
+
+    char jobtagbuf[80];
+    u_int i = 0;
+    char c;
+    for (const char* cp = jobtag; (c = *cp); cp++) {
+	if (i == sizeof (jobtagbuf)-2)		// truncate string
+	    break;
+	if (c == '\t')				// tabs are field delimiters
+	    c = ' ';
+	else if (c == '"')			// escape quote marks
+	    jobtagbuf[i++] = '\\';
+	jobtagbuf[i++] = c;
+    }
+    jobtagbuf[i] = '\0';
+
+    fxStr paramsbuf = fxStr::format("%u", params);
+    fxStr npagesbuf = fxStr::format("%d", npages);
+    fxStr durationbuf = fxStr::format("%s", fmtTime(duration));
+    fxStr conntimebuf = fxStr::format("%s", fmtTime(conntime));
+
+    fxStr callid_formatted = "";
+    for (i = 2; i < callid.size(); i++) {
+	if (i > 2) callid_formatted.append("::");
+	callid_formatted.append(callid[i]);
+    }
+
     if (fd >= 0) {
 	fxStackBuffer record;
-	char buf[80];
-	strftime(buf, sizeof (buf), "%D %H:%M", localtime(&start));
-	record.put(buf);			// $ 1 = time
+	record.put(timebuf);			// $ 1 = time
 	record.fput("\t%s", cmd);		// $ 2 = SEND|RECV|POLL|PAGE
 	record.fput("\t%s", commid);		// $ 3 = commid
 	record.fput("\t%s", device);		// $ 4 = device
 	record.fput("\t%s", jobid);		// $ 5 = jobid
-	u_int i = 0;
-	char c;
-	for (const char* cp = jobtag; (c = *cp); cp++) {
-	    if (i == sizeof (buf)-2)		// truncate string
-		break;
-	    if (c == '\t')			// tabs are field delimiters
-		c = ' ';
-	    else if (c == '"')			// escape quote marks
-		buf[i++] = '\\';
-	    buf[i++] = c;
-	}
-	buf[i] = '\0';
-	record.fput("\t\"%s\"", buf);		// $ 6 = jobtag
+	record.fput("\t\"%s\"", jobtagbuf);	// $ 6 = jobtag
 	record.fput("\t%s", user);		// $ 7 = sender
 	record.fput("\t\"%s\"", dest);		// $ 8 = dest
 	record.fput("\t\"%s\"", csi);		// $ 9 = csi
@@ -72,11 +87,6 @@
 	record.fput("\t\"%s\"", status);	// $14 = status
 	record.fput("\t\"%s\"", callid.size() > CallID::NAME ? (const char*) callid[1] : "");	// $15 = CallID2/CIDName
 	record.fput("\t\"%s\"", callid.size() > CallID::NUMBER ? (const char*) callid[0] : "");	// $16 = CallID1/CIDNumber
-	fxStr callid_formatted = "";
-	for (i = 2; i < callid.size(); i++) {
-	    if (i > 2) callid_formatted.append("::");
-	    callid_formatted.append(callid[i]);
-	}
 	record.fput("\t\"%s\"", (const char*) callid_formatted);	// $17 = CallID3 -> CallIDn
 	record.fput("\t\"%s\"", owner);					// $18 = owner
 	record.fput("\t\"%s\"", (const char*) faxdcs);			// $19 = DCS
@@ -85,5 +95,44 @@
 	ok = (Sys::write(fd, record, record.getLength()) == (ssize_t)record.getLength());
 	Sys::close(fd);				// implicit unlock
     }
+
+    /*
+     * Here we provide a hook for an external accounting
+     * facility, such as a database.
+     */
+    const char* argv[21];
+    argv[0] = "FaxAccounting";
+    argv[1] = timebuf;
+    argv[2] = cmd;
+    argv[3] = commid;
+    argv[4] = device;
+    argv[5] = jobid;
+    argv[6] = jobtagbuf;
+    argv[7] = user;
+    argv[8] = dest;
+    argv[9] = csi;
+    argv[10] = (const char*) paramsbuf;
+    argv[11] = (const char*) npagesbuf;
+    argv[12] = (const char*) durationbuf;
+    argv[13] = (const char*) conntimebuf;
+    argv[14] = status;
+    argv[15] = callid.size() > CallID::NAME ? (const char*) callid[1] : "";
+    argv[16] = callid.size() > CallID::NUMBER ? (const char*) callid[0] : "";
+    argv[17] = (const char*) callid_formatted;
+    argv[18] = owner;
+    argv[19] = (const char*) faxdcs;
+    argv[20] = NULL;
+    pid_t pid = fork();		// signal handling in some apps seems to require a fork here
+    switch (pid) {
+	case 0:
+	    Sys::execv("etc/FaxAccounting", (char* const*) argv);
+	    sleep(1);		// XXX give parent time
+	    _exit(127);
+	case -1:
+	    break;
+	default:
+	    Sys::waitpid(pid);
+	    break;
+    }
     return (ok);
 }
diff -Nru hylafax.orig/faxd/faxQueueApp.c++ hylafax/faxd/faxQueueApp.c++
--- hylafax.orig/faxd/faxQueueApp.c++	2005-11-14 08:33:31.000000000 -0800
+++ hylafax/faxd/faxQueueApp.c++	2005-11-15 15:02:17.523374808 -0800
@@ -1982,7 +1982,7 @@
 }
 
 void
-faxQueueApp::timeoutAccounting(Job& job, FaxRequest& req)
+faxQueueApp::queueAccounting(Job& job, FaxRequest& req, const char* type)
 {
     FaxAcctInfo ai;
     ai.jobid = (const char*) req.jobid;
@@ -1997,7 +1997,11 @@
     ai.csi = "";
     ai.npages = 0;
     ai.params = 0;
-    ai.status = "Kill time expired";
+    ai.status = "";
+    if (strstr(type, "UNSENT"))
+	ai.status = "Kill time expired";
+    else if (strstr(type, "SUBMIT"));
+	ai.status = "Submitted";
     CallID empty_callid;
     ai.callid = empty_callid;
     ai.owner = (const char*) req.owner;
@@ -2005,14 +2009,14 @@
     pid_t pid = fork();
     switch (pid) {
 	case -1:			// error
-	    if (!ai.record("UNSENT"))
-		logError("Error writing UNSENT accounting record, dest=%s",
-		    (const char*) ai.dest);
+	    if (!ai.record(type))
+		logError("Error writing %s accounting record, dest=%s",
+		    type, (const char*) ai.dest);
 	    break;
 	case 0:				// child
-	    if (!ai.record("UNSENT"))
+	    if (!ai.record(type))
 		logError("Error writing UNSENT accounting record, dest=%s",
-		    (const char*) ai.dest);
+		    type, (const char*) ai.dest);
 	    _exit(255);
 	    /*NOTREACHED*/
 	default:			// parent
@@ -2037,7 +2041,7 @@
 	job.state = FaxRequest::state_failed;
 	FaxRequest* req = readRequest(job);
 	if (req) {
-	    timeoutAccounting(job, *req);
+	    queueAccounting(job, *req, "UNSENT");
 	    req->notice = "Kill time expired";
 	    deleteRequest(job, req, Job::timedout, true);
 	}
@@ -2062,7 +2066,7 @@
     job.state = FaxRequest::state_failed;
     traceQueue(job, "KILL TIME EXPIRED");
     Trigger::post(Trigger::JOB_TIMEDOUT, job);
-    timeoutAccounting(job, req);
+    queueAccounting(job, req, "UNSENT");
     req.notice = "Kill time expired";
     deleteRequest(job, req, Job::timedout, true);
     setDead(job);
@@ -2073,7 +2077,7 @@
  * using the specified job description file.
  */
 bool
-faxQueueApp::submitJob(const fxStr& jobid, bool checkState)
+faxQueueApp::submitJob(const fxStr& jobid, bool checkState, bool doaccounting)
 {
     Job* job = Job::getJobByID(jobid);
     if (job) {
@@ -2127,6 +2131,7 @@
 	      req.state != FaxRequest::state_done &&
 	      req.state != FaxRequest::state_failed) {
 		status = submitJob(req, checkState);
+		if (doaccounting) queueAccounting(*job, req, "SUBMIT");
 	    } else if (reject) {
 		Job job(req);
 		job.state = FaxRequest::state_failed;
@@ -2788,7 +2793,7 @@
 	break;
     case 'S':				// submit an outbound job
 	traceServer("SUBMIT JOB %s", args);
-	if (status = submitJob(args))
+	if (status = submitJob(args, false, true))
 	    pokeScheduler();
 	break;
     case 'U':				// unreference file
diff -Nru hylafax.orig/faxd/faxQueueApp.h hylafax/faxd/faxQueueApp.h
--- hylafax.orig/faxd/faxQueueApp.h	2005-11-14 08:33:31.000000000 -0800
+++ hylafax/faxd/faxQueueApp.h	2005-11-15 15:02:38.742149064 -0800
@@ -204,7 +204,7 @@
     void	blockJob(Job&, FaxRequest&, const char*);
     void	delayJob(Job&, FaxRequest&, const char*, time_t);
     void	rejectJob(Job& job, FaxRequest& req, const fxStr& reason);
-    bool	submitJob(const fxStr& jobid, bool checkState = false);
+    bool	submitJob(const fxStr& jobid, bool checkState = false, bool doaccounting = false);
     bool	suspendJob(const fxStr& jobid, bool abortActive);
     void	rejectSubmission(Job&, FaxRequest&, const fxStr& reason);
 
@@ -217,7 +217,7 @@
     bool	submitJob(Job& job, FaxRequest& req, bool checkState = false);
     bool	suspendJob(Job& job, bool abortActive);
     bool	terminateJob(const fxStr& filename, JobStatus why);
-    void	timeoutAccounting(Job& job, FaxRequest&);
+    void	queueAccounting(Job& job, FaxRequest&, const char* type);
     void	timeoutJob(Job& job);
     void	timeoutJob(Job& job, FaxRequest&);
     void	runJob(Job& job);
diff -Nru hylafax.orig/man/wedged.1m hylafax/man/wedged.1m
--- hylafax.orig/man/wedged.1m	2005-11-14 08:33:31.000000000 -0800
+++ hylafax/man/wedged.1m	2005-11-15 14:23:30.365156632 -0800
@@ -74,7 +74,8 @@
 .P
 If there exists an executable file 
 .B ${SPOOL}/etc/resetmodem
-then that file will be executed upon execution of
+then that file will be executed (with the device name as the only
+argument) upon execution of
 the
 .I wedged
 script in an effort to recover the modem.
diff -Nru hylafax.orig/man/xferfaxlog.4f hylafax/man/xferfaxlog.4f
--- hylafax.orig/man/xferfaxlog.4f	2005-11-14 08:33:31.000000000 -0800
+++ hylafax/man/xferfaxlog.4f	2005-11-15 14:54:08.502717248 -0800
@@ -34,9 +34,11 @@
 The file
 .B etc/xferfaxlog
 contains information about inbound and outbound activities.
-The file contains one line per inbound or outbound call
-(except for facsimile documents retrieved by polling in which
-case multiple entries may be present for a single call).
+The file contains one line per inbound facsimile document or outbound job.
+The file also contains one line per expired job and one line per job submitted.
+If the modem config setting
+.I LogCalls
+is set to true, then the file also contains one line per received call.
 Lines are fixed-format,
 .I tab-separated
 .SM ASCII
@@ -44,13 +46,19 @@
 Each record of a facsimile transmission is of the form:
 .sp .5
 .ti +0.5i
-date \s-1SEND\s+1 commid modem jobid jobtag sender ``dest-number'' ``\s-1CSI\s+1'' params #pages jobtime conntime ``reason'' \fI<null>\fP \fI<null>\fP ``owner''
+date \s-1SEND\s+1 commid modem jobid jobtag sender ``dest-number'' ``\s-1CSI\s+1'' params #pages jobtime conntime ``reason'' \fI<null>\fP \fI<null>\fP ``owner'' ``dcs''
 .sp .5
 .PP
-A facsimile reception record is of the form:
+A facsimile document reception record is of the form:
 .sp .5
 .ti +0.5i
-date \s-1RECV\s+1 commid modem \fI<null>\fP \fI<null>\fP fax ``local-number'' ``\s-1TSI\s+1'' params #pages jobtime conntime ``reason'' ``CIDName'' ``CIDNumber'' \fI<null>\fP
+date \s-1RECV\s+1 commid modem \fI<null>\fP \fI<null>\fP fax ``local-number'' ``\s-1TSI\s+1'' params #pages jobtime conntime ``reason'' ``CIDName'' ``CIDNumber'' ``callid'' \fI<null>\fP ``dcs''
+.sp .5
+.PP
+Call records are of the form:
+.sp .5
+.ti +0.5i
+date \s-1CALL\s+1 commid modem \fI<null>\fP \fI<null>\fP fax ``local-number'' ``\s-1<null>\s+1'' 0 0 jobtime conntime ``\s-1<null>\s+1'' ``CIDName'' ``CIDNumber'' ``callid'' ....
 .sp .5
 .PP
 Each facsimile document retrieved by polling has a record of the form:
@@ -58,12 +66,23 @@
 .ti +0.5i
 date \s-1POLL\s+1 commid modem jobid jobtag sender ``dest-number'' ``\s-1TSI\s+1'' params #pages jobtime conntime ``reason'' \fI<null>\fP \fI<null>\fP \fI<null>\fP
 .sp .5
+.PP
 An alphanumeric pager request has a record of the form:
 .sp .5
 .ti +0.5i
 date \s-1PAGE\s+1 commid modem jobid jobtag sender ``dest-number'' ``\fI<null>\fP'' 0 0 jobtime conntime ``reason'' \fI<null>\fP \fI<null>\fP ``owner''
 .sp .5
 .PP
+Expired job records are of the form:
+.sp .5
+.ti +0.5i
+date \s-1UNSENT\s+1 ``\fI<null>\fP'' ``\fI<null>\fP'' jobid ....
+.PP
+Job submission records are of the form:
+.sp .5
+.ti +0.5i
+date \s-1SUBMIT\s+1 ``\fI<null>\fP'' ``\fI<null>\fP'' jobid ....
+.PP
 The following describes the fields in the above records:
 .TP 14
 .B date
@@ -138,6 +157,12 @@
 .TP 14
 .B owner
 The login name of the job owner.
+.TP 14
+.B callid
+The CallID values other than CIDName and CIDNumber formatted by ``::'' delimeters.
+.TP 14
+.B dcs
+The T.30 DCS string that was used in the facsimile communication.
 .PP
 Note that fields may have embedded blanks.
 Session parameters are encoded as a decimal number that contains
@@ -153,13 +178,23 @@
 .SM ASCII
 format was designed to be easy to process with programs like
 .IR awk (1).
+.PP
+If an executable named 
+.B ${SPOOL}/etc/FaxAccounting
+exists then it will be executed every time a record is written to
+.B etc/xferfaxlog.
+The record fields will each serve as separate command arguments.
+This is designed to allow attachment to external databases and other
+accounting mechanisms.
 .SH NOTES
 The sender field does not necessarily represent the submitter's 
 actual identity.  For example, it reflects the value given by
 the
 .IR sendfax (${MANNUM1_8})
 ``-f'' option.  Be cautious that this field is not utilized
-for auditing if the fax user base is not trusted.
+for auditing if the fax user base is not trusted.  Use
+.IR owner
+instead, if possible.
 .SH BUGS
 The date format will ``break'' in the year 2000.
 Information should be recorded on a per-page basis for facsimile



Project hosted by iFAX Solutions