Quota and Usage

February 18, 2025

Introduction

We want to give you visibility about how you use Jahia Cloud, so we've set up some important metrics to help explain your bills. These metrics aren't just for billing – they also show you how you're using Jahia Cloud. We've made sure each metric matches what you're doing, so you're charged fairly for what you use. Now, let's dig into the details of these metrics, like API Calls and Network Bandwidth, to help you understand your Jahia Cloud use better. 

Accessing the usage and quota information in Datadog is straightforward. Simply navigate to Datadog, click on "Monitors," then select "Dashboard List," and finally, search for "Cloud usage metrics."

Metrics : 

Page View

Means a view of a page on the Customer website that is being tracked by the Jahia Cloud Infrastructure, for example, if a visitor clicks reload after reaching the page, this is counted as an additional page view, and if a visitor navigates to a different page and then returns to the original page, a second page view is recorded as well. Page views are counted only for HTTP status code 200 responses that match the MIME type "text/html", while responses to other status codes (3XX, 4XX, 5XX, etc.) are not considered as page views.

Is not counted as page views:

  • Requests initiated by our monitoring system (Datadog)
  • Requests ending with the URI ".html.ajax"

Cette définition se matérialise par les filtres Datadog suivants :

Here is the corresponding filter that we use in Datadog to count the number of page views :

source:haproxy @haproxy.backend_name:bk_jahia @http.status_code:200 @haproxy.captured_response_headers:text/html* -@haproxy.captured_request_headers.User-Agent:"Datadog/Synthetics" -@http.url_details.path:*.html.ajax -@http.url:"/ping.jsp"

API Calls

Means all HTTP requests received by APIs running inside Jahia software, except for static resources. Additionally, shall be considered as an API call, a static resource when the call of the related static resource through the provided APIs generates a transformation. API Calls are counted if the URI (Uniform Resource Identifier) of the API request matches any of the specified patterns:

  • Ends with ".json"

  • Ends with “.xml"
  • Ends with ".do"
  • Ends with “.ajax”
  • Ends with ".jsp"
  • Contains "modules/graphql" in the URI.

Here is the corresponding filter that we use in Datadog to count the number of API Calls :

@logger.name:Render @http.url_details.path:(*.json OR *.xml OR *.do OR *.ajax OR *.jsp OR *modules\/graphql*)

Data Transfer Out

Refers to the data sent from all Jahia servers, determining how much content can be given to users yearly, including outgoing data (visitors, editors, and connectors). It's measured in terabytes (TB). When subscribing to the CDN the outgoing data will be used to get the total value of the Network bandwidth. 

Storage 

Represents the actual amount of storage space consumed by customer data and applications. It's the sum of the data, files, and assets stored on the Jahia Cloud platform.

Log Entries

Means the number of lines of logs generated by the application servers used by the customer's environment.

jCustomer Events

Represents the maximum number of interactions between visitors and the platform, which are stored in jCustomer software. These events can include page views, clicks, downloads, login, but are not limited to this list as customers can capture any custom interaction they might want. 

User 

Means any individual which could be human or machine, who is authorized by the customer to use the Jahia back-office. It is the number of unique users in Jahia who are part of the /groups/privileged group in the Jahia JCR. You can execute this groovy script to list the  number of unique users.  

import org.jahia.services.content.JCRNodeWrapper
import org.jahia.services.content.JCRSessionWrapper
import org.jahia.services.content.JCRTemplate
import org.jahia.services.content.decorator.JCRGroupNode
import org.jahia.services.content.decorator.JCRUserNode
import org.jahia.services.usermanager.JahiaGroupManagerService
import org.jahia.services.usermanager.JahiaUserManagerService
import javax.jcr.PropertyIterator
import javax.jcr.Property
import javax.jcr.Value

def collectMembers(JCRGroupNode groupNode, Set<String> members, Set<String> checkedGroups, JCRSessionWrapper session, def log, def startTime) {
    if (checkedGroups.contains(groupNode.getPath())) {
        return members
    }

    log.info("Check for group: {}", groupNode.getPath())
    checkedGroups.add(groupNode.getPath())
    for (JCRNodeWrapper member : groupNode.getMembers()) {
        if (member instanceof JCRUserNode) {
            members.add(member.getPath())
            if (members.size() % 10000 == 0) {
                log.info("{}s 10k members found ({}) - reset session cache.", (System.currentTimeMillis() - startTime) / 1000.0, members.size())
                session.refresh(false)
            }
        } else if (member instanceof JCRGroupNode) {
            collectMembers(member, members, checkedGroups, session, log, startTime)
        }
    }
    return members
}

long startTime = System.currentTimeMillis()
def jahiaUserManagerService = ServicesRegistry.getInstance().getJahiaUserManagerService()
log.info("jahiaUserManagerService: {}", jahiaUserManagerService)

JCRTemplate.getInstance().doExecuteWithSystemSession { session ->
    def groupNode = JahiaGroupManagerService.getInstance().lookupGroupByPath("/groups/privileged", session)

    if (!groupNode) {
        log.error("Group '/groups/privileged' not found")
        return
    }

    Set<String> members = collectMembers(groupNode, new HashSet<>(), new HashSet<>(), session, log, startTime)
    log.info("Privileged members found ({}):", members.size())

    members.each { memberPath ->
        String username = memberPath.substring(memberPath.lastIndexOf('/') + 1)
        JCRUserNode user = jahiaUserManagerService.lookupUser(username)

        if (user) {
            log.info("User: {}", username)

            PropertyIterator properties = user.getProperties()
            while (properties.hasNext()) {
                Property property = properties.nextProperty()

                if (property.isMultiple()) {
                    Value[] values = property.getValues()
                    def valueList = values.collect { it.getString() }
                    log.info("   - {} (multi-valued): {}", property.getName(), valueList)
                } else {
                    log.info("   - {}: {}", property.getName(), property.getString())
                }
            }
        } else {
            log.warn("User node not found for username: {}", username)
        }
    }

    log.info("Total execution time: {}s", (System.currentTimeMillis() - startTime) / 1000.0)
}