Add support for logging nested data structures with events (bug 332172). r=marria.

This commit is contained in:
bryner%brianryner.com 2006-04-01 00:06:41 +00:00
Родитель efff5fd986
Коммит aca3b4c231
6 изменённых файлов: 488 добавлений и 82 удалений

Просмотреть файл

@ -40,26 +40,130 @@
interface nsIPropertyBag;
[scriptable, uuid(30cad119-e248-4211-9682-8f95efe995c4)]
/**
* This file defines the interfaces for the Metrics Service.
*
* This service allows arbitrary types of events to be logged and uploaded
* to a server, based on server-configured collection parameters.
* The nsIMetricsService API provides an abstraction for the underlying XML
* data format.
*
* For more information about the data format and the built-in
* event collectors, see http://wiki.mozilla.org/Browser_Metrics.
*/
/**
* nsIMetricsEventItem represents a particular node of data to record
* in an event. Each item has a namespaced item name, a list of properties
* (key/value pairs), and an ordered list of child items. The child items
* need not be unique; an item may be repeated.
*/
[scriptable, uuid(880b8655-15a4-4b72-b6d4-6e3325bca4b6)]
interface nsIMetricsEventItem : nsISupports
{
/**
* The namespace for this item, which must be a valid XML namespace URI.
*/
readonly attribute DOMString itemNamespace;
/**
* The name of this item, which must be a valid XML tag name.
*/
readonly attribute DOMString itemName;
/**
* A PropertyBag containing the key/value pairs to be included in the item.
* JavaScript callers can simply set this to an object containing the
* key/value pairs as object properties.
*/
attribute nsIPropertyBag properties;
/**
* Returns the child event item at the given index.
*/
nsIMetricsEventItem childAt(in long index);
/**
* Returns the first occurrence of the given item in the child list,
* or -1 if the item is not in the child list.
*/
long indexOf(in nsIMetricsEventItem item);
/**
* Appends a child event item to this item.
*/
void appendChild(in nsIMetricsEventItem item);
/**
* Inserts a child event item at a given index, moving later items
* up by one position.
* @param item The new item to insert
* @param index The position in the array. If the index is equal to
* childCount, the new item will be appended.
* The index may not be greater than childCount.
*/
void insertChildAt(in nsIMetricsEventItem item, in long index);
/**
* Removes a child event item at the given index, moving all items
* stored at a higher position down one.
*/
void removeChildAt(in long index);
/**
* Replaces a child event item at the given index.
* @param newItem The new item
* @param index The position of the item to be replaced
*/
void replaceChildAt(in nsIMetricsEventItem newItem, in long index);
/**
* Clears all of the child items.
*/
void clearChildren();
/**
* The number of child event items
*/
readonly attribute long childCount;
};
[scriptable, uuid(ff320b73-ecd4-4e08-a09d-b37537770df6)]
interface nsIMetricsService : nsISupports
{
/**
* Logs an event using the given namespace. The event is recorded with a
* timestamp generated at the time at which this method is called.
* Creates a new EventItem object to hold event data.
* The event will not be logged until logEvent() is called.
* @param itemNamespace The new item's namespace
* @param itemName The new item's name
*
* @param eventNS
* The namespace of the event, which must be a valid XML namespace
* URI.
* @param eventName
* The name of the event, which must be a valid XML tag name.
* @param eventValues
* A PropertyBag containing the key/value pairs to be included
* in the event. JavaScript callers can simply pass in an object
* containing the key/value pairs as object properties.
* The "time" key is reserved for the event timestamp.
* @returns a new MetricsEventItem instance
*/
void logEvent(in DOMString eventNS, in DOMString event,
in nsIPropertyBag eventValues);
nsIMetricsEventItem createEventItem(in DOMString itemNamespace,
in DOMString itemName);
/**
* Logs an event using the given EventItem. The event is recorded with a
* timestamp generated at the time at which this method is called, and a
* session id for this instance of the application. The keys "time" and
* "sessionid" are reserved for this data.
*
* @param item
* The item to log. This item and its entire tree of child items
* will be logged as part of the event.
*/
void logEvent(in nsIMetricsEventItem item);
/**
* Constructs and logs an EventItem, using the given namespace, event name,
* and event properties. This is a more convenient version of logEvent() for
* the case where there are no child EventItems.
*
* @see nsIMetricsEventItem
*/
void logSimpleEvent(in DOMString eventNS, in DOMString event,
in nsIPropertyBag eventValues);
/**
* Flush data to disk.

Просмотреть файл

@ -65,6 +65,7 @@ REQUIRES = xpcom \
CPPSRCS = \
nsLoadCollector.cpp \
nsMetricsConfig.cpp \
nsMetricsEventItem.cpp \
nsMetricsModule.cpp \
nsMetricsService.cpp \
nsWindowCollector.cpp \

Просмотреть файл

@ -0,0 +1,157 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Metrics extension.
*
* The Initial Developer of the Original Code is Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsMetricsEventItem.h"
#include "nsIPropertyBag.h"
nsMetricsEventItem::nsMetricsEventItem(const nsAString &itemNamespace,
const nsAString &itemName)
: mNamespace(itemNamespace), mName(itemName)
{
}
nsMetricsEventItem::~nsMetricsEventItem()
{
}
NS_IMPL_ISUPPORTS1(nsMetricsEventItem, nsIMetricsEventItem)
NS_IMETHODIMP
nsMetricsEventItem::GetItemNamespace(nsAString &result)
{
result = mNamespace;
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::GetItemName(nsAString &result)
{
result = mName;
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::GetProperties(nsIPropertyBag **aProperties)
{
NS_IF_ADDREF(*aProperties = mProperties);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::SetProperties(nsIPropertyBag *aProperties)
{
mProperties = aProperties;
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::ChildAt(PRInt32 index, nsIMetricsEventItem **result)
{
if (index < 0 || index >= mChildren.Count()) {
return NS_ERROR_INVALID_ARG;
}
NS_ADDREF(*result = mChildren[index]);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::IndexOf(nsIMetricsEventItem *item, PRInt32 *result)
{
*result = mChildren.IndexOf(item);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::AppendChild(nsIMetricsEventItem *item)
{
NS_ENSURE_ARG_POINTER(item);
NS_ENSURE_TRUE(mChildren.AppendObject(item), NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::InsertChildAt(nsIMetricsEventItem *item, PRInt32 index)
{
NS_ENSURE_ARG_POINTER(item);
if (index < 0 || index > mChildren.Count()) {
return NS_ERROR_INVALID_ARG;
}
NS_ENSURE_TRUE(mChildren.InsertObjectAt(item, index),
NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::RemoveChildAt(PRInt32 index)
{
if (index < 0 || index >= mChildren.Count()) {
return NS_ERROR_INVALID_ARG;
}
mChildren.RemoveObjectAt(index);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::ReplaceChildAt(nsIMetricsEventItem *newItem, PRInt32 index)
{
NS_ENSURE_ARG_POINTER(newItem);
if (index < 0 || index >= mChildren.Count()) {
return NS_ERROR_INVALID_ARG;
}
mChildren.ReplaceObjectAt(newItem, index);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::ClearChildren()
{
mChildren.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsMetricsEventItem::GetChildCount(PRInt32 *childCount)
{
*childCount = mChildren.Count();
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,68 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Metrics extension.
*
* The Initial Developer of the Original Code is Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsMetricsEventItem_h__
#define nsMetricsEventItem_h__
#include "nsIMetricsService.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
class nsIPropertyBag;
// nsMetricsEventItem implements a single event item that can store properties.
class nsMetricsEventItem : public nsIMetricsEventItem
{
public:
nsMetricsEventItem(const nsAString &itemNamespace,
const nsAString &itemName);
NS_DECL_ISUPPORTS
NS_DECL_NSIMETRICSEVENTITEM
private:
~nsMetricsEventItem();
nsString mNamespace;
nsString mName;
nsCOMPtr<nsIPropertyBag> mProperties;
nsCOMArray<nsIMetricsEventItem> mChildren;
};
#endif // nsMetricsEventItem_h__

Просмотреть файл

@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsMetricsService.h"
#include "nsMetricsEventItem.h"
#include "nsXPCOM.h"
#include "nsServiceManagerUtils.h"
#include "nsDirectoryServiceUtils.h"
@ -113,11 +114,121 @@ NS_IMPL_ISUPPORTS6_CI(nsMetricsService, nsIMetricsService, nsIAboutModule,
nsITimerCallback)
NS_IMETHODIMP
nsMetricsService::LogEvent(const nsAString &eventNS,
const nsAString &eventName,
nsIPropertyBag *eventProperties)
nsMetricsService::CreateEventItem(const nsAString &itemNamespace,
const nsAString &itemName,
nsIMetricsEventItem **result)
{
NS_ENSURE_ARG_POINTER(eventProperties);
*result = nsnull;
nsMetricsEventItem *item = new nsMetricsEventItem(itemNamespace, itemName);
NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*result = item);
return NS_OK;
}
nsresult
nsMetricsService::BuildEventItem(nsIMetricsEventItem *item,
nsIDOMElement **itemElement)
{
*itemElement = nsnull;
nsAutoString itemNS, itemName;
item->GetItemNamespace(itemNS);
item->GetItemName(itemName);
nsCOMPtr<nsIDOMElement> element;
nsresult rv = mDocument->CreateElementNS(itemNS, itemName,
getter_AddRefs(element));
NS_ENSURE_SUCCESS(rv, rv);
// Attach the given properties as attributes.
nsCOMPtr<nsIPropertyBag> properties;
item->GetProperties(getter_AddRefs(properties));
if (properties) {
nsCOMPtr<nsISimpleEnumerator> enumerator;
rv = properties->GetEnumerator(getter_AddRefs(enumerator));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupports> propertySupports;
while (NS_SUCCEEDED(
enumerator->GetNext(getter_AddRefs(propertySupports)))) {
nsCOMPtr<nsIProperty> property = do_QueryInterface(propertySupports);
if (!property) {
NS_WARNING("PropertyBag enumerator has non-nsIProperty elements");
continue;
}
nsAutoString name;
rv = property->GetName(name);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get property name");
continue;
}
nsCOMPtr<nsIVariant> value;
rv = property->GetValue(getter_AddRefs(value));
if (NS_FAILED(rv) || !value) {
NS_WARNING("Failed to get property value");
continue;
}
// If the type is boolean, we want to use the strings "true" and "false",
// rather than "1" and "0" which is what nsVariant generates on its own.
PRUint16 dataType;
value->GetDataType(&dataType);
nsAutoString valueString;
if (dataType == nsIDataType::VTYPE_BOOL) {
PRBool valueBool;
rv = value->GetAsBool(&valueBool);
if (NS_FAILED(rv)) {
NS_WARNING("Variant has bool type but couldn't get bool value");
continue;
}
valueString = valueBool ? NS_LITERAL_STRING("true")
: NS_LITERAL_STRING("false");
} else {
rv = value->GetAsDOMString(valueString);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to convert property value to string");
continue;
}
}
rv = element->SetAttribute(name, valueString);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to set attribute value");
}
continue;
}
}
// Now recursively build the child event items
PRInt32 childCount = 0;
item->GetChildCount(&childCount);
for (PRInt32 i = 0; i < childCount; ++i) {
nsCOMPtr<nsIMetricsEventItem> childItem;
item->ChildAt(i, getter_AddRefs(childItem));
NS_ASSERTION(childItem, "The child list cannot contain null items");
nsCOMPtr<nsIDOMElement> childElement;
rv = BuildEventItem(childItem, getter_AddRefs(childElement));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> nodeReturn;
rv = element->AppendChild(childElement, getter_AddRefs(nodeReturn));
NS_ENSURE_SUCCESS(rv, rv);
}
element.swap(*itemElement);
return NS_OK;
}
NS_IMETHODIMP
nsMetricsService::LogEvent(nsIMetricsEventItem *item)
{
NS_ENSURE_ARG_POINTER(item);
if (mSuspendCount != 0) // Ignore events while suspended
return NS_OK;
@ -127,72 +238,18 @@ nsMetricsService::LogEvent(const nsAString &eventNS,
return NS_OK;
// Restrict the types of events logged
nsAutoString eventNS, eventName;
item->GetItemNamespace(eventNS);
item->GetItemName(eventName);
if (!mConfig.IsEventEnabled(eventNS, eventName))
return NS_OK;
// Create a DOM element for the event and append it to our document.
nsCOMPtr<nsIDOMElement> eventElement;
nsresult rv = mDocument->CreateElementNS(eventNS, eventName,
getter_AddRefs(eventElement));
nsresult rv = BuildEventItem(item, getter_AddRefs(eventElement));
NS_ENSURE_SUCCESS(rv, rv);
// Attach the given properties as attributes.
nsCOMPtr<nsISimpleEnumerator> enumerator;
rv = eventProperties->GetEnumerator(getter_AddRefs(enumerator));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupports> propertySupports;
while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(propertySupports)))) {
nsCOMPtr<nsIProperty> property = do_QueryInterface(propertySupports);
if (!property) {
NS_WARNING("PropertyBag enumerator has non-nsIProperty elements");
continue;
}
nsAutoString name;
rv = property->GetName(name);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get property name");
continue;
}
nsCOMPtr<nsIVariant> value;
rv = property->GetValue(getter_AddRefs(value));
if (NS_FAILED(rv) || !value) {
NS_WARNING("Failed to get property value");
continue;
}
// If the type is boolean, we want to use the strings "true" and "false",
// rather than "1" and "0" which is what nsVariant generates on its own.
PRUint16 dataType;
value->GetDataType(&dataType);
nsAutoString valueString;
if (dataType == nsIDataType::VTYPE_BOOL) {
PRBool valueBool;
rv = value->GetAsBool(&valueBool);
if (NS_FAILED(rv)) {
NS_WARNING("Variant has bool type but couldn't get bool value");
continue;
}
valueString = valueBool ? NS_LITERAL_STRING("true")
: NS_LITERAL_STRING("false");
} else {
rv = value->GetAsDOMString(valueString);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to convert property value to string");
continue;
}
}
rv = eventElement->SetAttribute(name, valueString);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to set attribute value");
}
continue;
}
// Add the event timestamp
nsAutoString timeString;
timeString.AppendInt(PR_Now() / PR_USEC_PER_SEC);
@ -213,6 +270,21 @@ nsMetricsService::LogEvent(const nsAString &eventNS,
return NS_OK;
}
NS_IMETHODIMP
nsMetricsService::LogSimpleEvent(const nsAString &eventNS,
const nsAString &eventName,
nsIPropertyBag *eventProperties)
{
NS_ENSURE_ARG_POINTER(eventProperties);
nsCOMPtr<nsIMetricsEventItem> item;
nsresult rv = CreateEventItem(eventNS, eventName, getter_AddRefs(item));
NS_ENSURE_SUCCESS(rv, rv);
item->SetProperties(eventProperties);
return LogEvent(item);
}
NS_IMETHODIMP
nsMetricsService::Flush()
{

Просмотреть файл

@ -101,9 +101,9 @@ public:
nsresult LogEvent(const nsAString &eventName,
nsHashPropertyBag *eventProperties)
{
return LogEvent(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
eventName,
NS_STATIC_CAST(nsIWritablePropertyBag*, eventProperties));
return LogSimpleEvent(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), eventName,
NS_STATIC_CAST(nsIWritablePropertyBag*,
eventProperties));
}
// Get the window id for the given DOMWindow. If a window id has not
@ -150,7 +150,11 @@ private:
return mConfig.IsEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
event);
}
// Builds up a DOMElement tree from the given item and its children
nsresult BuildEventItem(nsIMetricsEventItem *item,
nsIDOMElement **itemElement);
private:
// Pointer to the metrics service singleton
static nsMetricsService* sMetricsService;